librechat-data-provider 0.8.402 → 0.8.403

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.
Files changed (103) hide show
  1. package/dist/types/accessPermissions.d.ts +744 -0
  2. package/dist/types/actions.d.ts +118 -0
  3. package/dist/types/api-endpoints.d.ts +149 -0
  4. package/dist/types/artifacts.d.ts +97 -0
  5. package/dist/types/azure.d.ts +22 -0
  6. package/dist/types/bedrock.d.ts +1220 -0
  7. package/dist/types/config.d.ts +13916 -0
  8. package/dist/types/config.spec.d.ts +1 -0
  9. package/dist/types/createPayload.d.ts +5 -0
  10. package/dist/types/data-service.d.ts +284 -0
  11. package/dist/types/feedback.d.ts +36 -0
  12. package/dist/types/file-config.d.ts +263 -0
  13. package/dist/types/file-config.spec.d.ts +1 -0
  14. package/dist/types/generate.d.ts +597 -0
  15. package/dist/types/headers-helpers.d.ts +2 -0
  16. package/{src/index.ts → dist/types/index.d.ts} +0 -15
  17. package/dist/types/keys.d.ts +92 -0
  18. package/dist/types/mcp.d.ts +2760 -0
  19. package/dist/types/messages.d.ts +10 -0
  20. package/dist/types/models.d.ts +1677 -0
  21. package/dist/types/parameterSettings.d.ts +69 -0
  22. package/dist/types/parsers.d.ts +110 -0
  23. package/dist/types/permissions.d.ts +522 -0
  24. package/dist/types/react-query/react-query-service.d.ts +85 -0
  25. package/dist/types/request.d.ts +25 -0
  26. package/dist/types/roles.d.ts +554 -0
  27. package/dist/types/roles.spec.d.ts +1 -0
  28. package/dist/types/schemas.d.ts +4815 -0
  29. package/dist/types/schemas.spec.d.ts +1 -0
  30. package/dist/types/types/agents.d.ts +433 -0
  31. package/dist/types/types/assistants.d.ts +547 -0
  32. package/dist/types/types/files.d.ts +172 -0
  33. package/dist/types/types/graph.d.ts +135 -0
  34. package/{src/types/mcpServers.ts → dist/types/types/mcpServers.d.ts} +12 -18
  35. package/dist/types/types/mutations.d.ts +209 -0
  36. package/dist/types/types/queries.d.ts +169 -0
  37. package/dist/types/types/runs.d.ts +36 -0
  38. package/dist/types/types/web.d.ts +520 -0
  39. package/dist/types/types.d.ts +503 -0
  40. package/dist/types/utils.d.ts +12 -0
  41. package/package.json +4 -1
  42. package/babel.config.js +0 -4
  43. package/check_updates.sh +0 -52
  44. package/jest.config.js +0 -19
  45. package/react-query/package-lock.json +0 -292
  46. package/react-query/package.json +0 -10
  47. package/rollup.config.js +0 -74
  48. package/server-rollup.config.js +0 -40
  49. package/specs/actions.spec.ts +0 -2533
  50. package/specs/api-endpoints-subdir.spec.ts +0 -140
  51. package/specs/api-endpoints.spec.ts +0 -74
  52. package/specs/azure.spec.ts +0 -844
  53. package/specs/bedrock.spec.ts +0 -862
  54. package/specs/filetypes.spec.ts +0 -175
  55. package/specs/generate.spec.ts +0 -770
  56. package/specs/headers-helpers.spec.ts +0 -24
  57. package/specs/mcp.spec.ts +0 -147
  58. package/specs/openapiSpecs.ts +0 -524
  59. package/specs/parsers.spec.ts +0 -601
  60. package/specs/request-interceptor.spec.ts +0 -304
  61. package/specs/utils.spec.ts +0 -196
  62. package/src/accessPermissions.ts +0 -346
  63. package/src/actions.ts +0 -813
  64. package/src/api-endpoints.ts +0 -440
  65. package/src/artifacts.ts +0 -3104
  66. package/src/azure.ts +0 -328
  67. package/src/bedrock.ts +0 -425
  68. package/src/config.spec.ts +0 -315
  69. package/src/config.ts +0 -2006
  70. package/src/createPayload.ts +0 -46
  71. package/src/data-service.ts +0 -1087
  72. package/src/feedback.ts +0 -141
  73. package/src/file-config.spec.ts +0 -1248
  74. package/src/file-config.ts +0 -764
  75. package/src/generate.ts +0 -634
  76. package/src/headers-helpers.ts +0 -13
  77. package/src/keys.ts +0 -99
  78. package/src/mcp.ts +0 -271
  79. package/src/messages.ts +0 -50
  80. package/src/models.ts +0 -69
  81. package/src/parameterSettings.ts +0 -1111
  82. package/src/parsers.ts +0 -563
  83. package/src/permissions.ts +0 -188
  84. package/src/react-query/react-query-service.ts +0 -566
  85. package/src/request.ts +0 -171
  86. package/src/roles.spec.ts +0 -132
  87. package/src/roles.ts +0 -225
  88. package/src/schemas.spec.ts +0 -355
  89. package/src/schemas.ts +0 -1234
  90. package/src/types/agents.ts +0 -470
  91. package/src/types/assistants.ts +0 -654
  92. package/src/types/files.ts +0 -191
  93. package/src/types/graph.ts +0 -145
  94. package/src/types/mutations.ts +0 -422
  95. package/src/types/queries.ts +0 -208
  96. package/src/types/runs.ts +0 -40
  97. package/src/types/web.ts +0 -588
  98. package/src/types.ts +0 -676
  99. package/src/utils.ts +0 -85
  100. package/tsconfig.json +0 -28
  101. package/tsconfig.spec.json +0 -10
  102. /package/{src/react-query/index.ts → dist/types/react-query/index.d.ts} +0 -0
  103. /package/{src/types/index.ts → dist/types/types/index.d.ts} +0 -0
@@ -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
- });