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