librechat-data-provider 0.8.402 → 0.8.404
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/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-query/index.es.js +1 -1
- package/dist/react-query/index.es.js.map +1 -1
- package/dist/types/accessPermissions.d.ts +744 -0
- package/dist/types/actions.d.ts +118 -0
- package/dist/types/api-endpoints.d.ts +150 -0
- package/dist/types/artifacts.d.ts +97 -0
- package/dist/types/azure.d.ts +22 -0
- package/dist/types/bedrock.d.ts +1220 -0
- package/dist/types/config.d.ts +14849 -0
- package/dist/types/config.spec.d.ts +1 -0
- package/dist/types/createPayload.d.ts +5 -0
- package/dist/types/data-service.d.ts +287 -0
- package/dist/types/feedback.d.ts +36 -0
- package/dist/types/file-config.d.ts +263 -0
- package/dist/types/file-config.spec.d.ts +1 -0
- package/dist/types/generate.d.ts +597 -0
- package/dist/types/headers-helpers.d.ts +2 -0
- package/{src/index.ts → dist/types/index.d.ts} +0 -15
- package/dist/types/keys.d.ts +92 -0
- package/dist/types/mcp.d.ts +2760 -0
- package/dist/types/messages.d.ts +10 -0
- package/dist/types/models.d.ts +1547 -0
- package/dist/types/parameterSettings.d.ts +69 -0
- package/dist/types/parsers.d.ts +110 -0
- package/dist/types/permissions.d.ts +522 -0
- package/dist/types/react-query/react-query-service.d.ts +85 -0
- package/dist/types/request.d.ts +25 -0
- package/dist/types/roles.d.ts +554 -0
- package/dist/types/roles.spec.d.ts +1 -0
- package/dist/types/schemas.d.ts +5110 -0
- package/dist/types/schemas.spec.d.ts +1 -0
- package/dist/types/types/agents.d.ts +433 -0
- package/dist/types/types/assistants.d.ts +547 -0
- package/dist/types/types/files.d.ts +172 -0
- package/dist/types/types/graph.d.ts +135 -0
- package/{src/types/mcpServers.ts → dist/types/types/mcpServers.d.ts} +12 -18
- package/dist/types/types/mutations.d.ts +209 -0
- package/dist/types/types/queries.d.ts +169 -0
- package/dist/types/types/runs.d.ts +36 -0
- package/dist/types/types/web.d.ts +520 -0
- package/dist/types/types.d.ts +503 -0
- package/dist/types/utils.d.ts +12 -0
- package/package.json +5 -1
- package/babel.config.js +0 -4
- package/check_updates.sh +0 -52
- package/jest.config.js +0 -19
- package/react-query/package-lock.json +0 -292
- package/react-query/package.json +0 -10
- package/rollup.config.js +0 -74
- package/server-rollup.config.js +0 -40
- package/specs/actions.spec.ts +0 -2533
- package/specs/api-endpoints-subdir.spec.ts +0 -140
- package/specs/api-endpoints.spec.ts +0 -74
- package/specs/azure.spec.ts +0 -844
- package/specs/bedrock.spec.ts +0 -862
- package/specs/filetypes.spec.ts +0 -175
- package/specs/generate.spec.ts +0 -770
- package/specs/headers-helpers.spec.ts +0 -24
- package/specs/mcp.spec.ts +0 -147
- package/specs/openapiSpecs.ts +0 -524
- package/specs/parsers.spec.ts +0 -601
- package/specs/request-interceptor.spec.ts +0 -304
- package/specs/utils.spec.ts +0 -196
- package/src/accessPermissions.ts +0 -346
- package/src/actions.ts +0 -813
- package/src/api-endpoints.ts +0 -440
- package/src/artifacts.ts +0 -3104
- package/src/azure.ts +0 -328
- package/src/bedrock.ts +0 -425
- package/src/config.spec.ts +0 -315
- package/src/config.ts +0 -2006
- package/src/createPayload.ts +0 -46
- package/src/data-service.ts +0 -1087
- package/src/feedback.ts +0 -141
- package/src/file-config.spec.ts +0 -1248
- package/src/file-config.ts +0 -764
- package/src/generate.ts +0 -634
- package/src/headers-helpers.ts +0 -13
- package/src/keys.ts +0 -99
- package/src/mcp.ts +0 -271
- package/src/messages.ts +0 -50
- package/src/models.ts +0 -69
- package/src/parameterSettings.ts +0 -1111
- package/src/parsers.ts +0 -563
- package/src/permissions.ts +0 -188
- package/src/react-query/react-query-service.ts +0 -566
- package/src/request.ts +0 -171
- package/src/roles.spec.ts +0 -132
- package/src/roles.ts +0 -225
- package/src/schemas.spec.ts +0 -355
- package/src/schemas.ts +0 -1234
- package/src/types/agents.ts +0 -470
- package/src/types/assistants.ts +0 -654
- package/src/types/files.ts +0 -191
- package/src/types/graph.ts +0 -145
- package/src/types/mutations.ts +0 -422
- package/src/types/queries.ts +0 -208
- package/src/types/runs.ts +0 -40
- package/src/types/web.ts +0 -588
- package/src/types.ts +0 -676
- package/src/utils.ts +0 -85
- package/tsconfig.json +0 -28
- package/tsconfig.spec.json +0 -10
- /package/{src/react-query/index.ts → dist/types/react-query/index.d.ts} +0 -0
- /package/{src/types/index.ts → dist/types/types/index.d.ts} +0 -0
package/specs/parsers.spec.ts
DELETED
|
@@ -1,601 +0,0 @@
|
|
|
1
|
-
import { replaceSpecialVars, parseConvo, parseCompactConvo, parseTextParts } from '../src/parsers';
|
|
2
|
-
import { specialVariables } from '../src/config';
|
|
3
|
-
import { EModelEndpoint } from '../src/schemas';
|
|
4
|
-
import { ContentTypes } from '../src/types/runs';
|
|
5
|
-
import type { TMessageContentParts } from '../src/types/assistants';
|
|
6
|
-
import type { TUser, TConversation } from '../src/types';
|
|
7
|
-
|
|
8
|
-
// Mock dayjs module with consistent date/time values regardless of environment
|
|
9
|
-
jest.mock('dayjs', () => {
|
|
10
|
-
const mockDayjs = () => ({
|
|
11
|
-
format: (format: string) => {
|
|
12
|
-
if (format === 'YYYY-MM-DD') {
|
|
13
|
-
return '2024-04-29';
|
|
14
|
-
}
|
|
15
|
-
if (format === 'YYYY-MM-DD HH:mm:ss Z') {
|
|
16
|
-
return '2024-04-29 12:34:56 -04:00';
|
|
17
|
-
}
|
|
18
|
-
if (format === 'dddd') {
|
|
19
|
-
return 'Monday';
|
|
20
|
-
}
|
|
21
|
-
throw new Error(
|
|
22
|
-
`Unhandled dayjs().format() call in mock: "${format}". Update the mock in parsers.spec.ts`,
|
|
23
|
-
);
|
|
24
|
-
},
|
|
25
|
-
toISOString: () => '2024-04-29T16:34:56.000Z',
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
mockDayjs.extend = jest.fn();
|
|
29
|
-
|
|
30
|
-
return mockDayjs;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('replaceSpecialVars', () => {
|
|
34
|
-
// Create a partial user object for testing
|
|
35
|
-
const mockUser = {
|
|
36
|
-
name: 'Test User',
|
|
37
|
-
id: 'user123',
|
|
38
|
-
} as TUser;
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
jest.clearAllMocks();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('should return the original text if text is empty', () => {
|
|
45
|
-
expect(replaceSpecialVars({ text: '' })).toBe('');
|
|
46
|
-
expect(replaceSpecialVars({ text: null as unknown as string })).toBe(null);
|
|
47
|
-
expect(replaceSpecialVars({ text: undefined as unknown as string })).toBe(undefined);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('should replace {{current_date}} with the current date', () => {
|
|
51
|
-
const result = replaceSpecialVars({ text: 'Today is {{current_date}}' });
|
|
52
|
-
expect(result).toBe('Today is 2024-04-29 (Monday)');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('should replace {{current_datetime}} with the current datetime', () => {
|
|
56
|
-
const result = replaceSpecialVars({ text: 'Now is {{current_datetime}}' });
|
|
57
|
-
expect(result).toBe('Now is 2024-04-29 12:34:56 -04:00 (Monday)');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('should replace {{iso_datetime}} with the ISO datetime', () => {
|
|
61
|
-
const result = replaceSpecialVars({ text: 'ISO time: {{iso_datetime}}' });
|
|
62
|
-
expect(result).toBe('ISO time: 2024-04-29T16:34:56.000Z');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('should replace {{current_user}} with the user name if provided', () => {
|
|
66
|
-
const result = replaceSpecialVars({
|
|
67
|
-
text: 'Hello {{current_user}}!',
|
|
68
|
-
user: mockUser,
|
|
69
|
-
});
|
|
70
|
-
expect(result).toBe('Hello Test User!');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('should not replace {{current_user}} if user is not provided', () => {
|
|
74
|
-
const result = replaceSpecialVars({
|
|
75
|
-
text: 'Hello {{current_user}}!',
|
|
76
|
-
});
|
|
77
|
-
expect(result).toBe('Hello {{current_user}}!');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('should not replace {{current_user}} if user has no name', () => {
|
|
81
|
-
const result = replaceSpecialVars({
|
|
82
|
-
text: 'Hello {{current_user}}!',
|
|
83
|
-
user: { id: 'user123' } as TUser,
|
|
84
|
-
});
|
|
85
|
-
expect(result).toBe('Hello {{current_user}}!');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('should handle multiple replacements in the same text', () => {
|
|
89
|
-
const result = replaceSpecialVars({
|
|
90
|
-
text: 'Hello {{current_user}}! Today is {{current_date}} and the time is {{current_datetime}}. ISO: {{iso_datetime}}',
|
|
91
|
-
user: mockUser,
|
|
92
|
-
});
|
|
93
|
-
expect(result).toBe(
|
|
94
|
-
'Hello Test User! Today is 2024-04-29 (Monday) and the time is 2024-04-29 12:34:56 -04:00 (Monday). ISO: 2024-04-29T16:34:56.000Z',
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('should be case-insensitive when replacing variables', () => {
|
|
99
|
-
const result = replaceSpecialVars({
|
|
100
|
-
text: 'Date: {{CURRENT_DATE}}, User: {{Current_User}}',
|
|
101
|
-
user: mockUser,
|
|
102
|
-
});
|
|
103
|
-
expect(result).toBe('Date: 2024-04-29 (Monday), User: Test User');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('should confirm all specialVariables from config.ts get parsed', () => {
|
|
107
|
-
// Create a text that includes all special variables
|
|
108
|
-
const specialVarsText = Object.keys(specialVariables)
|
|
109
|
-
.map((key) => `{{${key}}}`)
|
|
110
|
-
.join(' ');
|
|
111
|
-
|
|
112
|
-
const result = replaceSpecialVars({
|
|
113
|
-
text: specialVarsText,
|
|
114
|
-
user: mockUser,
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Verify none of the original variable placeholders remain in the result
|
|
118
|
-
Object.keys(specialVariables).forEach((key) => {
|
|
119
|
-
const placeholder = `{{${key}}}`;
|
|
120
|
-
expect(result).not.toContain(placeholder);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// Verify the expected replacements
|
|
124
|
-
expect(result).toContain('2024-04-29 (Monday)'); // current_date
|
|
125
|
-
expect(result).toContain('2024-04-29 12:34:56 -04:00 (Monday)'); // current_datetime
|
|
126
|
-
expect(result).toContain('2024-04-29T16:34:56.000Z'); // iso_datetime
|
|
127
|
-
expect(result).toContain('Test User'); // current_user
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe('parseCompactConvo', () => {
|
|
132
|
-
describe('iconURL security sanitization', () => {
|
|
133
|
-
test('should strip iconURL from OpenAI endpoint conversation input', () => {
|
|
134
|
-
const maliciousIconURL = 'https://evil-tracker.example.com/pixel.png?user=victim';
|
|
135
|
-
const conversation: Partial<TConversation> = {
|
|
136
|
-
model: 'gpt-4',
|
|
137
|
-
iconURL: maliciousIconURL,
|
|
138
|
-
endpoint: EModelEndpoint.openAI,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const result = parseCompactConvo({
|
|
142
|
-
endpoint: EModelEndpoint.openAI,
|
|
143
|
-
conversation,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
expect(result).not.toBeNull();
|
|
147
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
148
|
-
expect(result?.model).toBe('gpt-4');
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test('should strip iconURL from agents endpoint conversation input', () => {
|
|
152
|
-
const maliciousIconURL = 'https://evil-tracker.example.com/pixel.png';
|
|
153
|
-
const conversation: Partial<TConversation> = {
|
|
154
|
-
agent_id: 'agent_123',
|
|
155
|
-
iconURL: maliciousIconURL,
|
|
156
|
-
endpoint: EModelEndpoint.agents,
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const result = parseCompactConvo({
|
|
160
|
-
endpoint: EModelEndpoint.agents,
|
|
161
|
-
conversation,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
expect(result).not.toBeNull();
|
|
165
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
166
|
-
expect(result?.agent_id).toBe('agent_123');
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test('should strip iconURL from anthropic endpoint conversation input', () => {
|
|
170
|
-
const maliciousIconURL = 'https://tracker.malicious.com/beacon.gif';
|
|
171
|
-
const conversation: Partial<TConversation> = {
|
|
172
|
-
model: 'claude-3-opus',
|
|
173
|
-
iconURL: maliciousIconURL,
|
|
174
|
-
endpoint: EModelEndpoint.anthropic,
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const result = parseCompactConvo({
|
|
178
|
-
endpoint: EModelEndpoint.anthropic,
|
|
179
|
-
conversation,
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(result).not.toBeNull();
|
|
183
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
184
|
-
expect(result?.model).toBe('claude-3-opus');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test('should strip iconURL from google endpoint conversation input', () => {
|
|
188
|
-
const maliciousIconURL = 'https://tracking.example.com/spy.png';
|
|
189
|
-
const conversation: Partial<TConversation> = {
|
|
190
|
-
model: 'gemini-pro',
|
|
191
|
-
iconURL: maliciousIconURL,
|
|
192
|
-
endpoint: EModelEndpoint.google,
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const result = parseCompactConvo({
|
|
196
|
-
endpoint: EModelEndpoint.google,
|
|
197
|
-
conversation,
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(result).not.toBeNull();
|
|
201
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
202
|
-
expect(result?.model).toBe('gemini-pro');
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test('should strip iconURL from assistants endpoint conversation input', () => {
|
|
206
|
-
const maliciousIconURL = 'https://evil.com/track.png';
|
|
207
|
-
const conversation: Partial<TConversation> = {
|
|
208
|
-
assistant_id: 'asst_123',
|
|
209
|
-
iconURL: maliciousIconURL,
|
|
210
|
-
endpoint: EModelEndpoint.assistants,
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const result = parseCompactConvo({
|
|
214
|
-
endpoint: EModelEndpoint.assistants,
|
|
215
|
-
conversation,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
expect(result).not.toBeNull();
|
|
219
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
220
|
-
expect(result?.assistant_id).toBe('asst_123');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test('should preserve other conversation properties while stripping iconURL', () => {
|
|
224
|
-
const conversation: Partial<TConversation> = {
|
|
225
|
-
model: 'gpt-4',
|
|
226
|
-
iconURL: 'https://malicious.com/track.png',
|
|
227
|
-
endpoint: EModelEndpoint.openAI,
|
|
228
|
-
temperature: 0.7,
|
|
229
|
-
top_p: 0.9,
|
|
230
|
-
promptPrefix: 'You are a helpful assistant.',
|
|
231
|
-
maxContextTokens: 4000,
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const result = parseCompactConvo({
|
|
235
|
-
endpoint: EModelEndpoint.openAI,
|
|
236
|
-
conversation,
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
expect(result).not.toBeNull();
|
|
240
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
241
|
-
expect(result?.model).toBe('gpt-4');
|
|
242
|
-
expect(result?.temperature).toBe(0.7);
|
|
243
|
-
expect(result?.top_p).toBe(0.9);
|
|
244
|
-
expect(result?.promptPrefix).toBe('You are a helpful assistant.');
|
|
245
|
-
expect(result?.maxContextTokens).toBe(4000);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
test('should handle conversation without iconURL (no error)', () => {
|
|
249
|
-
const conversation: Partial<TConversation> = {
|
|
250
|
-
model: 'gpt-4',
|
|
251
|
-
endpoint: EModelEndpoint.openAI,
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const result = parseCompactConvo({
|
|
255
|
-
endpoint: EModelEndpoint.openAI,
|
|
256
|
-
conversation,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
expect(result).not.toBeNull();
|
|
260
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
261
|
-
expect(result?.model).toBe('gpt-4');
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
describe('parseConvo - defaultParamsEndpoint', () => {
|
|
267
|
-
test('should strip maxOutputTokens for custom endpoint without defaultParamsEndpoint', () => {
|
|
268
|
-
const conversation: Partial<TConversation> = {
|
|
269
|
-
model: 'anthropic/claude-opus-4.5',
|
|
270
|
-
temperature: 0.7,
|
|
271
|
-
maxOutputTokens: 8192,
|
|
272
|
-
maxContextTokens: 50000,
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
const result = parseConvo({
|
|
276
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
277
|
-
endpointType: EModelEndpoint.custom,
|
|
278
|
-
conversation,
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
expect(result).not.toBeNull();
|
|
282
|
-
expect(result?.temperature).toBe(0.7);
|
|
283
|
-
expect(result?.maxContextTokens).toBe(50000);
|
|
284
|
-
expect(result?.maxOutputTokens).toBeUndefined();
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test('should preserve maxOutputTokens when defaultParamsEndpoint is anthropic', () => {
|
|
288
|
-
const conversation: Partial<TConversation> = {
|
|
289
|
-
model: 'anthropic/claude-opus-4.5',
|
|
290
|
-
temperature: 0.7,
|
|
291
|
-
maxOutputTokens: 8192,
|
|
292
|
-
topP: 0.9,
|
|
293
|
-
topK: 40,
|
|
294
|
-
maxContextTokens: 50000,
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
const result = parseConvo({
|
|
298
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
299
|
-
endpointType: EModelEndpoint.custom,
|
|
300
|
-
conversation,
|
|
301
|
-
defaultParamsEndpoint: EModelEndpoint.anthropic,
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
expect(result).not.toBeNull();
|
|
305
|
-
expect(result?.model).toBe('anthropic/claude-opus-4.5');
|
|
306
|
-
expect(result?.temperature).toBe(0.7);
|
|
307
|
-
expect(result?.maxOutputTokens).toBe(8192);
|
|
308
|
-
expect(result?.topP).toBe(0.9);
|
|
309
|
-
expect(result?.topK).toBe(40);
|
|
310
|
-
expect(result?.maxContextTokens).toBe(50000);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
test('should strip OpenAI-specific fields when defaultParamsEndpoint is anthropic', () => {
|
|
314
|
-
const conversation: Partial<TConversation> = {
|
|
315
|
-
model: 'anthropic/claude-opus-4.5',
|
|
316
|
-
temperature: 0.7,
|
|
317
|
-
max_tokens: 4096,
|
|
318
|
-
top_p: 0.9,
|
|
319
|
-
presence_penalty: 0.5,
|
|
320
|
-
frequency_penalty: 0.3,
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const result = parseConvo({
|
|
324
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
325
|
-
endpointType: EModelEndpoint.custom,
|
|
326
|
-
conversation,
|
|
327
|
-
defaultParamsEndpoint: EModelEndpoint.anthropic,
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
expect(result).not.toBeNull();
|
|
331
|
-
expect(result?.temperature).toBe(0.7);
|
|
332
|
-
expect(result?.max_tokens).toBeUndefined();
|
|
333
|
-
expect(result?.top_p).toBeUndefined();
|
|
334
|
-
expect(result?.presence_penalty).toBeUndefined();
|
|
335
|
-
expect(result?.frequency_penalty).toBeUndefined();
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
test('should preserve max_tokens when defaultParamsEndpoint is not set (OpenAI default)', () => {
|
|
339
|
-
const conversation: Partial<TConversation> = {
|
|
340
|
-
model: 'gpt-4o',
|
|
341
|
-
temperature: 0.7,
|
|
342
|
-
max_tokens: 4096,
|
|
343
|
-
top_p: 0.9,
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const result = parseConvo({
|
|
347
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
348
|
-
endpointType: EModelEndpoint.custom,
|
|
349
|
-
conversation,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
expect(result).not.toBeNull();
|
|
353
|
-
expect(result?.max_tokens).toBe(4096);
|
|
354
|
-
expect(result?.top_p).toBe(0.9);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test('should preserve Google-specific fields when defaultParamsEndpoint is google', () => {
|
|
358
|
-
const conversation: Partial<TConversation> = {
|
|
359
|
-
model: 'gemini-pro',
|
|
360
|
-
temperature: 0.7,
|
|
361
|
-
maxOutputTokens: 8192,
|
|
362
|
-
topP: 0.9,
|
|
363
|
-
topK: 40,
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
const result = parseConvo({
|
|
367
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
368
|
-
endpointType: EModelEndpoint.custom,
|
|
369
|
-
conversation,
|
|
370
|
-
defaultParamsEndpoint: EModelEndpoint.google,
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
expect(result).not.toBeNull();
|
|
374
|
-
expect(result?.maxOutputTokens).toBe(8192);
|
|
375
|
-
expect(result?.topP).toBe(0.9);
|
|
376
|
-
expect(result?.topK).toBe(40);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test('should not strip fields from non-custom endpoints that already have a schema', () => {
|
|
380
|
-
const conversation: Partial<TConversation> = {
|
|
381
|
-
model: 'gpt-4o',
|
|
382
|
-
temperature: 0.7,
|
|
383
|
-
max_tokens: 4096,
|
|
384
|
-
top_p: 0.9,
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
const result = parseConvo({
|
|
388
|
-
endpoint: EModelEndpoint.openAI,
|
|
389
|
-
conversation,
|
|
390
|
-
defaultParamsEndpoint: EModelEndpoint.anthropic,
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
expect(result).not.toBeNull();
|
|
394
|
-
expect(result?.max_tokens).toBe(4096);
|
|
395
|
-
expect(result?.top_p).toBe(0.9);
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
test('should not carry bedrock region to custom endpoint without defaultParamsEndpoint', () => {
|
|
399
|
-
const conversation: Partial<TConversation> = {
|
|
400
|
-
model: 'gpt-4o',
|
|
401
|
-
temperature: 0.7,
|
|
402
|
-
region: 'us-east-1',
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
const result = parseConvo({
|
|
406
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
407
|
-
endpointType: EModelEndpoint.custom,
|
|
408
|
-
conversation,
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
expect(result).not.toBeNull();
|
|
412
|
-
expect(result?.temperature).toBe(0.7);
|
|
413
|
-
expect(result?.region).toBeUndefined();
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
test('should fall back to endpointType schema when defaultParamsEndpoint is invalid', () => {
|
|
417
|
-
const conversation: Partial<TConversation> = {
|
|
418
|
-
model: 'gpt-4o',
|
|
419
|
-
temperature: 0.7,
|
|
420
|
-
max_tokens: 4096,
|
|
421
|
-
maxOutputTokens: 8192,
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
const result = parseConvo({
|
|
425
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
426
|
-
endpointType: EModelEndpoint.custom,
|
|
427
|
-
conversation,
|
|
428
|
-
defaultParamsEndpoint: 'nonexistent_endpoint',
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
expect(result).not.toBeNull();
|
|
432
|
-
expect(result?.max_tokens).toBe(4096);
|
|
433
|
-
expect(result?.maxOutputTokens).toBeUndefined();
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
describe('parseCompactConvo - defaultParamsEndpoint', () => {
|
|
438
|
-
test('should strip maxOutputTokens for custom endpoint without defaultParamsEndpoint', () => {
|
|
439
|
-
const conversation: Partial<TConversation> = {
|
|
440
|
-
model: 'anthropic/claude-opus-4.5',
|
|
441
|
-
temperature: 0.7,
|
|
442
|
-
maxOutputTokens: 8192,
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
const result = parseCompactConvo({
|
|
446
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
447
|
-
endpointType: EModelEndpoint.custom,
|
|
448
|
-
conversation,
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
expect(result).not.toBeNull();
|
|
452
|
-
expect(result?.temperature).toBe(0.7);
|
|
453
|
-
expect(result?.maxOutputTokens).toBeUndefined();
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
test('should preserve maxOutputTokens when defaultParamsEndpoint is anthropic', () => {
|
|
457
|
-
const conversation: Partial<TConversation> = {
|
|
458
|
-
model: 'anthropic/claude-opus-4.5',
|
|
459
|
-
temperature: 0.7,
|
|
460
|
-
maxOutputTokens: 8192,
|
|
461
|
-
topP: 0.9,
|
|
462
|
-
maxContextTokens: 50000,
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
const result = parseCompactConvo({
|
|
466
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
467
|
-
endpointType: EModelEndpoint.custom,
|
|
468
|
-
conversation,
|
|
469
|
-
defaultParamsEndpoint: EModelEndpoint.anthropic,
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
expect(result).not.toBeNull();
|
|
473
|
-
expect(result?.maxOutputTokens).toBe(8192);
|
|
474
|
-
expect(result?.topP).toBe(0.9);
|
|
475
|
-
expect(result?.maxContextTokens).toBe(50000);
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
test('should strip iconURL even when defaultParamsEndpoint is set', () => {
|
|
479
|
-
const conversation: Partial<TConversation> = {
|
|
480
|
-
model: 'anthropic/claude-opus-4.5',
|
|
481
|
-
iconURL: 'https://malicious.com/track.png',
|
|
482
|
-
maxOutputTokens: 8192,
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
const result = parseCompactConvo({
|
|
486
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
487
|
-
endpointType: EModelEndpoint.custom,
|
|
488
|
-
conversation,
|
|
489
|
-
defaultParamsEndpoint: EModelEndpoint.anthropic,
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
expect(result).not.toBeNull();
|
|
493
|
-
expect(result?.['iconURL']).toBeUndefined();
|
|
494
|
-
expect(result?.maxOutputTokens).toBe(8192);
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
test('should fall back to endpointType when defaultParamsEndpoint is null', () => {
|
|
498
|
-
const conversation: Partial<TConversation> = {
|
|
499
|
-
model: 'gpt-4o',
|
|
500
|
-
max_tokens: 4096,
|
|
501
|
-
maxOutputTokens: 8192,
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
const result = parseCompactConvo({
|
|
505
|
-
endpoint: 'MyCustomEndpoint' as EModelEndpoint,
|
|
506
|
-
endpointType: EModelEndpoint.custom,
|
|
507
|
-
conversation,
|
|
508
|
-
defaultParamsEndpoint: null,
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
expect(result).not.toBeNull();
|
|
512
|
-
expect(result?.max_tokens).toBe(4096);
|
|
513
|
-
expect(result?.maxOutputTokens).toBeUndefined();
|
|
514
|
-
});
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
describe('parseTextParts', () => {
|
|
518
|
-
test('should concatenate text parts', () => {
|
|
519
|
-
const parts: TMessageContentParts[] = [
|
|
520
|
-
{ type: ContentTypes.TEXT, text: 'Hello' },
|
|
521
|
-
{ type: ContentTypes.TEXT, text: 'World' },
|
|
522
|
-
];
|
|
523
|
-
expect(parseTextParts(parts)).toBe('Hello World');
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
test('should handle text parts with object-style text values', () => {
|
|
527
|
-
const parts: TMessageContentParts[] = [
|
|
528
|
-
{ type: ContentTypes.TEXT, text: { value: 'structured text' } },
|
|
529
|
-
];
|
|
530
|
-
expect(parseTextParts(parts)).toBe('structured text');
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
test('should include think parts by default', () => {
|
|
534
|
-
const parts: TMessageContentParts[] = [
|
|
535
|
-
{ type: ContentTypes.TEXT, text: 'Answer:' },
|
|
536
|
-
{ type: ContentTypes.THINK, think: 'reasoning step' },
|
|
537
|
-
];
|
|
538
|
-
expect(parseTextParts(parts)).toBe('Answer: reasoning step');
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
test('should skip think parts when skipReasoning is true', () => {
|
|
542
|
-
const parts: TMessageContentParts[] = [
|
|
543
|
-
{ type: ContentTypes.THINK, think: 'internal reasoning' },
|
|
544
|
-
{ type: ContentTypes.TEXT, text: 'visible answer' },
|
|
545
|
-
];
|
|
546
|
-
expect(parseTextParts(parts, true)).toBe('visible answer');
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
test('should skip non-text/think part types', () => {
|
|
550
|
-
const parts: TMessageContentParts[] = [
|
|
551
|
-
{ type: ContentTypes.TEXT, text: 'before' },
|
|
552
|
-
{ type: ContentTypes.IMAGE_FILE } as TMessageContentParts,
|
|
553
|
-
{ type: ContentTypes.TEXT, text: 'after' },
|
|
554
|
-
];
|
|
555
|
-
expect(parseTextParts(parts)).toBe('before after');
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
test('should handle undefined elements in the content parts array', () => {
|
|
559
|
-
const parts: Array<TMessageContentParts | undefined> = [
|
|
560
|
-
{ type: ContentTypes.TEXT, text: 'first' },
|
|
561
|
-
undefined,
|
|
562
|
-
{ type: ContentTypes.TEXT, text: 'third' },
|
|
563
|
-
];
|
|
564
|
-
expect(parseTextParts(parts)).toBe('first third');
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
test('should handle multiple consecutive undefined elements', () => {
|
|
568
|
-
const parts: Array<TMessageContentParts | undefined> = [
|
|
569
|
-
undefined,
|
|
570
|
-
undefined,
|
|
571
|
-
{ type: ContentTypes.TEXT, text: 'only text' },
|
|
572
|
-
undefined,
|
|
573
|
-
];
|
|
574
|
-
expect(parseTextParts(parts)).toBe('only text');
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
test('should handle an array of all undefined elements', () => {
|
|
578
|
-
const parts: Array<TMessageContentParts | undefined> = [undefined, undefined, undefined];
|
|
579
|
-
expect(parseTextParts(parts)).toBe('');
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
test('should handle parts with missing type property', () => {
|
|
583
|
-
const parts: Array<TMessageContentParts | undefined> = [
|
|
584
|
-
{ text: 'no type field' } as unknown as TMessageContentParts,
|
|
585
|
-
{ type: ContentTypes.TEXT, text: 'valid' },
|
|
586
|
-
];
|
|
587
|
-
expect(parseTextParts(parts)).toBe('valid');
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
test('should return empty string for empty array', () => {
|
|
591
|
-
expect(parseTextParts([])).toBe('');
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
test('should not add extra spaces when parts already have spacing', () => {
|
|
595
|
-
const parts: TMessageContentParts[] = [
|
|
596
|
-
{ type: ContentTypes.TEXT, text: 'Hello ' },
|
|
597
|
-
{ type: ContentTypes.TEXT, text: 'World' },
|
|
598
|
-
];
|
|
599
|
-
expect(parseTextParts(parts)).toBe('Hello World');
|
|
600
|
-
});
|
|
601
|
-
});
|