ai.matey.middleware 0.2.0
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/LICENSE +21 -0
- package/dist/cjs/caching.js +226 -0
- package/dist/cjs/caching.js.map +1 -0
- package/dist/cjs/conversation-history.js +213 -0
- package/dist/cjs/conversation-history.js.map +1 -0
- package/dist/cjs/cost-tracking.js +355 -0
- package/dist/cjs/cost-tracking.js.map +1 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/logging.js +174 -0
- package/dist/cjs/logging.js.map +1 -0
- package/dist/cjs/opentelemetry.js +499 -0
- package/dist/cjs/opentelemetry.js.map +1 -0
- package/dist/cjs/retry.js +205 -0
- package/dist/cjs/retry.js.map +1 -0
- package/dist/cjs/security.js +175 -0
- package/dist/cjs/security.js.map +1 -0
- package/dist/cjs/telemetry.js +216 -0
- package/dist/cjs/telemetry.js.map +1 -0
- package/dist/cjs/transform.js +284 -0
- package/dist/cjs/transform.js.map +1 -0
- package/dist/cjs/validation.js +506 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/caching.js +221 -0
- package/dist/esm/caching.js.map +1 -0
- package/dist/esm/conversation-history.js +207 -0
- package/dist/esm/conversation-history.js.map +1 -0
- package/dist/esm/cost-tracking.js +347 -0
- package/dist/esm/cost-tracking.js.map +1 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logging.js +171 -0
- package/dist/esm/logging.js.map +1 -0
- package/dist/esm/opentelemetry.js +458 -0
- package/dist/esm/opentelemetry.js.map +1 -0
- package/dist/esm/retry.js +198 -0
- package/dist/esm/retry.js.map +1 -0
- package/dist/esm/security.js +169 -0
- package/dist/esm/security.js.map +1 -0
- package/dist/esm/telemetry.js +210 -0
- package/dist/esm/telemetry.js.map +1 -0
- package/dist/esm/transform.js +272 -0
- package/dist/esm/transform.js.map +1 -0
- package/dist/esm/validation.js +494 -0
- package/dist/esm/validation.js.map +1 -0
- package/dist/types/caching.d.ts +98 -0
- package/dist/types/caching.d.ts.map +1 -0
- package/dist/types/conversation-history.d.ts +188 -0
- package/dist/types/conversation-history.d.ts.map +1 -0
- package/dist/types/cost-tracking.d.ts +262 -0
- package/dist/types/cost-tracking.d.ts.map +1 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/logging.d.ts +82 -0
- package/dist/types/logging.d.ts.map +1 -0
- package/dist/types/opentelemetry.d.ts +219 -0
- package/dist/types/opentelemetry.d.ts.map +1 -0
- package/dist/types/retry.d.ts +86 -0
- package/dist/types/retry.d.ts.map +1 -0
- package/dist/types/security.d.ts +120 -0
- package/dist/types/security.d.ts.map +1 -0
- package/dist/types/telemetry.d.ts +120 -0
- package/dist/types/telemetry.d.ts.map +1 -0
- package/dist/types/transform.d.ts +184 -0
- package/dist/types/transform.d.ts.map +1 -0
- package/dist/types/validation.d.ts +356 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/package.json +203 -0
- package/readme.md +103 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caching Middleware
|
|
3
|
+
*
|
|
4
|
+
* Caches responses with TTL-based expiration and LRU eviction.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { createHash } from 'crypto';
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Default Key Generator
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Default cache key generator.
|
|
14
|
+
*
|
|
15
|
+
* Generates cache key from model, messages, and parameters.
|
|
16
|
+
* Excludes metadata and streaming flag.
|
|
17
|
+
*/
|
|
18
|
+
function defaultKeyGenerator(request) {
|
|
19
|
+
// Create a stable cache key from request
|
|
20
|
+
const cacheableData = {
|
|
21
|
+
model: request.parameters?.model,
|
|
22
|
+
messages: request.messages,
|
|
23
|
+
temperature: request.parameters?.temperature,
|
|
24
|
+
maxTokens: request.parameters?.maxTokens,
|
|
25
|
+
topP: request.parameters?.topP,
|
|
26
|
+
topK: request.parameters?.topK,
|
|
27
|
+
stopSequences: request.parameters?.stopSequences,
|
|
28
|
+
tools: request.tools,
|
|
29
|
+
toolChoice: request.toolChoice,
|
|
30
|
+
};
|
|
31
|
+
// Generate hash
|
|
32
|
+
const json = JSON.stringify(cacheableData);
|
|
33
|
+
return createHash('sha256').update(json).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* In-memory cache storage with LRU eviction.
|
|
37
|
+
*/
|
|
38
|
+
export class InMemoryCacheStorage {
|
|
39
|
+
cache = new Map();
|
|
40
|
+
accessOrder = [];
|
|
41
|
+
maxSize;
|
|
42
|
+
constructor(maxSize = 1000) {
|
|
43
|
+
this.maxSize = maxSize;
|
|
44
|
+
}
|
|
45
|
+
get(key) {
|
|
46
|
+
const entry = this.cache.get(key);
|
|
47
|
+
if (!entry) {
|
|
48
|
+
return Promise.resolve(undefined);
|
|
49
|
+
}
|
|
50
|
+
// Check if expired
|
|
51
|
+
if (Date.now() > entry.expiresAt) {
|
|
52
|
+
this.cache.delete(key);
|
|
53
|
+
this.removeFromAccessOrder(key);
|
|
54
|
+
return Promise.resolve(undefined);
|
|
55
|
+
}
|
|
56
|
+
// Update access order (LRU)
|
|
57
|
+
this.updateAccessOrder(key);
|
|
58
|
+
return Promise.resolve(entry.value);
|
|
59
|
+
}
|
|
60
|
+
set(key, value, ttl = 3600000) {
|
|
61
|
+
// Evict if at max size
|
|
62
|
+
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
63
|
+
this.evictLRU();
|
|
64
|
+
}
|
|
65
|
+
// Store entry
|
|
66
|
+
this.cache.set(key, {
|
|
67
|
+
value,
|
|
68
|
+
expiresAt: Date.now() + ttl,
|
|
69
|
+
});
|
|
70
|
+
// Update access order
|
|
71
|
+
this.updateAccessOrder(key);
|
|
72
|
+
return Promise.resolve();
|
|
73
|
+
}
|
|
74
|
+
has(key) {
|
|
75
|
+
const entry = this.cache.get(key);
|
|
76
|
+
if (!entry) {
|
|
77
|
+
return Promise.resolve(false);
|
|
78
|
+
}
|
|
79
|
+
// Check if expired
|
|
80
|
+
if (Date.now() > entry.expiresAt) {
|
|
81
|
+
this.cache.delete(key);
|
|
82
|
+
this.removeFromAccessOrder(key);
|
|
83
|
+
return Promise.resolve(false);
|
|
84
|
+
}
|
|
85
|
+
return Promise.resolve(true);
|
|
86
|
+
}
|
|
87
|
+
delete(key) {
|
|
88
|
+
this.removeFromAccessOrder(key);
|
|
89
|
+
return Promise.resolve(this.cache.delete(key));
|
|
90
|
+
}
|
|
91
|
+
clear() {
|
|
92
|
+
this.cache.clear();
|
|
93
|
+
this.accessOrder = [];
|
|
94
|
+
return Promise.resolve();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get cache statistics.
|
|
98
|
+
*/
|
|
99
|
+
getStats() {
|
|
100
|
+
return {
|
|
101
|
+
size: this.cache.size,
|
|
102
|
+
maxSize: this.maxSize,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update LRU access order.
|
|
107
|
+
*/
|
|
108
|
+
updateAccessOrder(key) {
|
|
109
|
+
// Remove from current position
|
|
110
|
+
this.removeFromAccessOrder(key);
|
|
111
|
+
// Add to end (most recently used)
|
|
112
|
+
this.accessOrder.push(key);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Remove key from access order.
|
|
116
|
+
*/
|
|
117
|
+
removeFromAccessOrder(key) {
|
|
118
|
+
const index = this.accessOrder.indexOf(key);
|
|
119
|
+
if (index !== -1) {
|
|
120
|
+
this.accessOrder.splice(index, 1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Evict least recently used entry.
|
|
125
|
+
*/
|
|
126
|
+
evictLRU() {
|
|
127
|
+
if (this.accessOrder.length === 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Get least recently used key (first in array)
|
|
131
|
+
const lruKey = this.accessOrder[0];
|
|
132
|
+
if (lruKey) {
|
|
133
|
+
this.cache.delete(lruKey);
|
|
134
|
+
this.accessOrder.shift();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Clean up expired entries.
|
|
139
|
+
*/
|
|
140
|
+
cleanup() {
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
const expiredKeys = [];
|
|
143
|
+
// Use Array.from to avoid iteration issues
|
|
144
|
+
for (const [key, entry] of Array.from(this.cache.entries())) {
|
|
145
|
+
if (now > entry.expiresAt) {
|
|
146
|
+
expiredKeys.push(key);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const key of expiredKeys) {
|
|
150
|
+
this.cache.delete(key);
|
|
151
|
+
this.removeFromAccessOrder(key);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Middleware Factory
|
|
157
|
+
// ============================================================================
|
|
158
|
+
/**
|
|
159
|
+
* Create caching middleware.
|
|
160
|
+
*
|
|
161
|
+
* Caches responses with TTL-based expiration and LRU eviction.
|
|
162
|
+
*
|
|
163
|
+
* @param config Caching configuration
|
|
164
|
+
* @returns Caching middleware
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* const caching = createCachingMiddleware({
|
|
169
|
+
* ttl: 3600000, // 1 hour
|
|
170
|
+
* maxSize: 1000,
|
|
171
|
+
* cacheStreaming: false
|
|
172
|
+
* });
|
|
173
|
+
*
|
|
174
|
+
* bridge.use(caching);
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export function createCachingMiddleware(config = {}) {
|
|
178
|
+
const { keyGenerator = defaultKeyGenerator, ttl = 3600000, // 1 hour
|
|
179
|
+
maxSize = 1000, storage = new InMemoryCacheStorage(maxSize), cacheStreaming = false, } = config;
|
|
180
|
+
return async (context, next) => {
|
|
181
|
+
// Skip caching for streaming requests unless explicitly enabled
|
|
182
|
+
if (context.request.stream && !cacheStreaming) {
|
|
183
|
+
return next();
|
|
184
|
+
}
|
|
185
|
+
// Generate cache key
|
|
186
|
+
const cacheKey = keyGenerator(context.request);
|
|
187
|
+
// Check cache
|
|
188
|
+
const cachedResponse = await storage.get(cacheKey);
|
|
189
|
+
if (cachedResponse) {
|
|
190
|
+
// Cache hit - add cache metadata
|
|
191
|
+
return {
|
|
192
|
+
...cachedResponse,
|
|
193
|
+
metadata: {
|
|
194
|
+
...cachedResponse.metadata,
|
|
195
|
+
custom: {
|
|
196
|
+
...cachedResponse.metadata.custom,
|
|
197
|
+
cacheHit: true,
|
|
198
|
+
cacheKey,
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Cache miss - call next middleware/handler
|
|
204
|
+
const response = await next();
|
|
205
|
+
// Store in cache
|
|
206
|
+
await storage.set(cacheKey, response, ttl);
|
|
207
|
+
// Add cache metadata
|
|
208
|
+
return {
|
|
209
|
+
...response,
|
|
210
|
+
metadata: {
|
|
211
|
+
...response.metadata,
|
|
212
|
+
custom: {
|
|
213
|
+
...response.metadata.custom,
|
|
214
|
+
cacheHit: false,
|
|
215
|
+
cacheKey,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=caching.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caching.js","sourceRoot":"","sources":["../../src/caching.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation History Middleware
|
|
3
|
+
*
|
|
4
|
+
* Manages conversation history across requests with configurable trimming strategies.
|
|
5
|
+
* Maintains a global conversation history that can be accessed and modified by requests.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { trimHistory, shouldTrimHistory } from 'ai.matey.utils';
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Middleware Factory
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Create conversation history middleware.
|
|
15
|
+
*
|
|
16
|
+
* Maintains a global conversation history across requests. Automatically prepends
|
|
17
|
+
* history to requests and appends responses to history after completion.
|
|
18
|
+
*
|
|
19
|
+
* @param config Conversation history configuration
|
|
20
|
+
* @returns Conversation history middleware and manager
|
|
21
|
+
*
|
|
22
|
+
* @example Basic Usage
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const { middleware, manager } = createConversationHistoryMiddleware({
|
|
25
|
+
* maxHistorySize: 10,
|
|
26
|
+
* strategy: 'smart'
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* bridge.use(middleware);
|
|
30
|
+
*
|
|
31
|
+
* // Access history
|
|
32
|
+
* console.log('Pairs:', manager.getPairCount());
|
|
33
|
+
* console.log('History:', manager.getHistory());
|
|
34
|
+
*
|
|
35
|
+
* // Clear history when needed
|
|
36
|
+
* manager.clear();
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example With Initial System Message
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const { middleware } = createConversationHistoryMiddleware({
|
|
42
|
+
* initialHistory: [
|
|
43
|
+
* { role: 'system', content: 'You are a helpful assistant.' }
|
|
44
|
+
* ],
|
|
45
|
+
* maxHistorySize: 5,
|
|
46
|
+
* strategy: 'smart' // Preserves system message
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @example Stateless Mode (No History)
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const { middleware } = createConversationHistoryMiddleware({
|
|
53
|
+
* maxHistorySize: 0 // No history tracking
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example Custom Message Filtering
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const { middleware } = createConversationHistoryMiddleware({
|
|
60
|
+
* maxHistorySize: 10,
|
|
61
|
+
* // Only track user and assistant messages, ignore system
|
|
62
|
+
* messageFilter: (msg) => msg.role !== 'system'
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function createConversationHistoryMiddleware(config = {}) {
|
|
67
|
+
const { maxHistorySize = -1, strategy = 'smart', prependHistory = true, trackResponses = true, initialHistory = [], messageFilter = () => true, } = config;
|
|
68
|
+
// Internal conversation history state
|
|
69
|
+
let history = [...initialHistory];
|
|
70
|
+
// Create manager for external access
|
|
71
|
+
const manager = {
|
|
72
|
+
getHistory: () => [...history],
|
|
73
|
+
addMessage: (message) => {
|
|
74
|
+
if (messageFilter(message)) {
|
|
75
|
+
history.push(message);
|
|
76
|
+
manager.trim();
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
addMessages: (messages) => {
|
|
80
|
+
for (const message of messages) {
|
|
81
|
+
if (messageFilter(message)) {
|
|
82
|
+
history.push(message);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
manager.trim();
|
|
86
|
+
},
|
|
87
|
+
clear: () => {
|
|
88
|
+
history = [];
|
|
89
|
+
},
|
|
90
|
+
setHistory: (newHistory) => {
|
|
91
|
+
history = [...newHistory];
|
|
92
|
+
manager.trim();
|
|
93
|
+
},
|
|
94
|
+
getPairCount: () => {
|
|
95
|
+
const nonSystemMessages = history.filter((m) => m.role !== 'system');
|
|
96
|
+
return Math.floor(nonSystemMessages.length / 2);
|
|
97
|
+
},
|
|
98
|
+
trim: () => {
|
|
99
|
+
if (shouldTrimHistory(history, maxHistorySize, strategy)) {
|
|
100
|
+
history = trimHistory(history, maxHistorySize, strategy);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
// Create middleware
|
|
105
|
+
const middleware = async (context, next) => {
|
|
106
|
+
let request = context.request;
|
|
107
|
+
// Prepend history to request if enabled
|
|
108
|
+
if (prependHistory && maxHistorySize !== 0) {
|
|
109
|
+
const currentHistory = manager.getHistory();
|
|
110
|
+
// Merge history with request messages
|
|
111
|
+
// Filter out duplicate system messages from request if history already has them
|
|
112
|
+
const requestMessages = request.messages.filter((msg) => {
|
|
113
|
+
// Keep all non-system messages
|
|
114
|
+
if (msg.role !== 'system') {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
// For system messages, only keep if not already in history
|
|
118
|
+
const existingSystemMessage = currentHistory.find((h) => h.role === 'system' && h.content === msg.content);
|
|
119
|
+
return !existingSystemMessage;
|
|
120
|
+
});
|
|
121
|
+
request = {
|
|
122
|
+
...request,
|
|
123
|
+
messages: [...currentHistory, ...requestMessages],
|
|
124
|
+
};
|
|
125
|
+
context.request = request;
|
|
126
|
+
}
|
|
127
|
+
// Call next middleware/handler
|
|
128
|
+
const response = await next();
|
|
129
|
+
// Track response in history if enabled
|
|
130
|
+
if (trackResponses && maxHistorySize !== 0) {
|
|
131
|
+
// Add the new user message(s) from this request to history
|
|
132
|
+
const newUserMessages = context.request.messages.filter((msg) => msg.role === 'user' || msg.role === 'assistant');
|
|
133
|
+
// Find messages that aren't already in history
|
|
134
|
+
for (const msg of newUserMessages) {
|
|
135
|
+
const alreadyInHistory = history.some((h) => h.role === msg.role &&
|
|
136
|
+
h.content === msg.content &&
|
|
137
|
+
h.timestamp === msg.timestamp);
|
|
138
|
+
if (!alreadyInHistory) {
|
|
139
|
+
manager.addMessage(msg);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Add assistant response to history
|
|
143
|
+
if (response.message) {
|
|
144
|
+
manager.addMessage(response.message);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return response;
|
|
148
|
+
};
|
|
149
|
+
return { middleware, manager };
|
|
150
|
+
}
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// Helper Functions
|
|
153
|
+
// ============================================================================
|
|
154
|
+
/**
|
|
155
|
+
* Create a simple conversation history middleware with minimal config.
|
|
156
|
+
*
|
|
157
|
+
* @param maxHistorySize Maximum number of message pairs to keep
|
|
158
|
+
* @returns Conversation history middleware
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* // Keep last 5 message pairs
|
|
163
|
+
* bridge.use(simpleConversationHistory(5));
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export function simpleConversationHistory(maxHistorySize = 10) {
|
|
167
|
+
const { middleware } = createConversationHistoryMiddleware({ maxHistorySize });
|
|
168
|
+
return middleware;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Create a stateless conversation history middleware.
|
|
172
|
+
* Useful for explicitly disabling history in a pipeline.
|
|
173
|
+
*
|
|
174
|
+
* @returns Stateless middleware
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* // Explicitly disable history
|
|
179
|
+
* bridge.use(statelessConversation());
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
export function statelessConversation() {
|
|
183
|
+
const { middleware } = createConversationHistoryMiddleware({ maxHistorySize: 0 });
|
|
184
|
+
return middleware;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Create a conversation history middleware that only tracks user/assistant pairs.
|
|
188
|
+
* System messages from requests are passed through but not tracked.
|
|
189
|
+
*
|
|
190
|
+
* @param maxHistorySize Maximum number of message pairs
|
|
191
|
+
* @returns Conversation history middleware
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* // Track conversation but not system messages
|
|
196
|
+
* bridge.use(conversationOnlyHistory(10));
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
export function conversationOnlyHistory(maxHistorySize = 10) {
|
|
200
|
+
const { middleware } = createConversationHistoryMiddleware({
|
|
201
|
+
maxHistorySize,
|
|
202
|
+
strategy: 'fifo',
|
|
203
|
+
messageFilter: (msg) => msg.role !== 'system',
|
|
204
|
+
});
|
|
205
|
+
return middleware;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=conversation-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-history.js","sourceRoot":"","sources":["../../src/conversation-history.ts"],"names":[],"mappings":"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"}
|