@ank1015/providers 0.0.1

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 (99) hide show
  1. package/README.md +453 -0
  2. package/biome.json +43 -0
  3. package/dist/agent/agent-loop.d.ts +5 -0
  4. package/dist/agent/agent-loop.d.ts.map +1 -0
  5. package/dist/agent/agent-loop.js +219 -0
  6. package/dist/agent/agent-loop.js.map +1 -0
  7. package/dist/agent/types.d.ts +67 -0
  8. package/dist/agent/types.d.ts.map +1 -0
  9. package/dist/agent/types.js +3 -0
  10. package/dist/agent/types.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +29 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/models.d.ts +3 -0
  16. package/dist/models.d.ts.map +1 -0
  17. package/dist/models.generated.d.ts +247 -0
  18. package/dist/models.generated.d.ts.map +1 -0
  19. package/dist/models.generated.js +315 -0
  20. package/dist/models.generated.js.map +1 -0
  21. package/dist/models.js +41 -0
  22. package/dist/models.js.map +1 -0
  23. package/dist/providers/convert.d.ts +6 -0
  24. package/dist/providers/convert.d.ts.map +1 -0
  25. package/dist/providers/convert.js +207 -0
  26. package/dist/providers/convert.js.map +1 -0
  27. package/dist/providers/google.d.ts +26 -0
  28. package/dist/providers/google.d.ts.map +1 -0
  29. package/dist/providers/google.js +434 -0
  30. package/dist/providers/google.js.map +1 -0
  31. package/dist/providers/openai.d.ts +17 -0
  32. package/dist/providers/openai.d.ts.map +1 -0
  33. package/dist/providers/openai.js +396 -0
  34. package/dist/providers/openai.js.map +1 -0
  35. package/dist/stream.d.ts +4 -0
  36. package/dist/stream.d.ts.map +1 -0
  37. package/dist/stream.js +40 -0
  38. package/dist/stream.js.map +1 -0
  39. package/dist/test-google-agent-loop.d.ts +2 -0
  40. package/dist/test-google-agent-loop.d.ts.map +1 -0
  41. package/dist/test-google-agent-loop.js +186 -0
  42. package/dist/test-google-agent-loop.js.map +1 -0
  43. package/dist/test-google.d.ts +2 -0
  44. package/dist/test-google.d.ts.map +1 -0
  45. package/dist/test-google.js +41 -0
  46. package/dist/test-google.js.map +1 -0
  47. package/dist/types.d.ts +187 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +10 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/utils/event-stream.d.ts +16 -0
  52. package/dist/utils/event-stream.d.ts.map +1 -0
  53. package/dist/utils/event-stream.js +61 -0
  54. package/dist/utils/event-stream.js.map +1 -0
  55. package/dist/utils/json-parse.d.ts +9 -0
  56. package/dist/utils/json-parse.d.ts.map +1 -0
  57. package/dist/utils/json-parse.js +32 -0
  58. package/dist/utils/json-parse.js.map +1 -0
  59. package/dist/utils/sanitize-unicode.d.ts +22 -0
  60. package/dist/utils/sanitize-unicode.d.ts.map +1 -0
  61. package/dist/utils/sanitize-unicode.js +29 -0
  62. package/dist/utils/sanitize-unicode.js.map +1 -0
  63. package/dist/utils/validation.d.ts +11 -0
  64. package/dist/utils/validation.d.ts.map +1 -0
  65. package/dist/utils/validation.js +61 -0
  66. package/dist/utils/validation.js.map +1 -0
  67. package/package.json +33 -0
  68. package/src/agent/agent-loop.ts +275 -0
  69. package/src/agent/types.ts +80 -0
  70. package/src/index.ts +72 -0
  71. package/src/models.generated.ts +314 -0
  72. package/src/models.ts +45 -0
  73. package/src/providers/convert.ts +222 -0
  74. package/src/providers/google.ts +496 -0
  75. package/src/providers/openai.ts +437 -0
  76. package/src/stream.ts +60 -0
  77. package/src/types.ts +198 -0
  78. package/src/utils/event-stream.ts +60 -0
  79. package/src/utils/json-parse.ts +28 -0
  80. package/src/utils/sanitize-unicode.ts +25 -0
  81. package/src/utils/validation.ts +69 -0
  82. package/test/core/agent-loop.test.ts +958 -0
  83. package/test/core/stream.test.ts +409 -0
  84. package/test/data/red-circle.png +0 -0
  85. package/test/data/superintelligentwill.pdf +0 -0
  86. package/test/edge-cases/general.test.ts +565 -0
  87. package/test/integration/e2e.test.ts +530 -0
  88. package/test/models/cost.test.ts +499 -0
  89. package/test/models/registry.test.ts +298 -0
  90. package/test/providers/convert.test.ts +846 -0
  91. package/test/providers/google-schema.test.ts +666 -0
  92. package/test/providers/google-stream.test.ts +369 -0
  93. package/test/providers/openai-stream.test.ts +251 -0
  94. package/test/utils/event-stream.test.ts +289 -0
  95. package/test/utils/json-parse.test.ts +344 -0
  96. package/test/utils/sanitize-unicode.test.ts +329 -0
  97. package/test/utils/validation.test.ts +614 -0
  98. package/tsconfig.json +21 -0
  99. package/vitest.config.ts +9 -0
@@ -0,0 +1,329 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { sanitizeSurrogates } from '../../src/utils/sanitize-unicode';
3
+
4
+ describe('sanitizeSurrogates', () => {
5
+ describe('Valid text (no changes)', () => {
6
+ it('should leave valid ASCII text unchanged', () => {
7
+ const text = 'Hello, World!';
8
+ const result = sanitizeSurrogates(text);
9
+
10
+ expect(result).toBe(text);
11
+ });
12
+
13
+ it('should leave valid Unicode text unchanged', () => {
14
+ const text = 'Hello 世界 مرحبا мир';
15
+ const result = sanitizeSurrogates(text);
16
+
17
+ expect(result).toBe(text);
18
+ });
19
+
20
+ it('should preserve valid emoji (paired surrogates)', () => {
21
+ const text = 'Hello 😀 👋 🌍 ✨';
22
+ const result = sanitizeSurrogates(text);
23
+
24
+ expect(result).toBe(text);
25
+ expect(result).toContain('😀');
26
+ expect(result).toContain('👋');
27
+ });
28
+
29
+ it('should preserve multiple emojis in sequence', () => {
30
+ const text = '🎉🎊🎈🎁';
31
+ const result = sanitizeSurrogates(text);
32
+
33
+ expect(result).toBe(text);
34
+ });
35
+
36
+ it('should preserve complex emoji with modifiers', () => {
37
+ const text = '👨‍👩‍👧‍👦'; // Family emoji with ZWJ
38
+ const result = sanitizeSurrogates(text);
39
+
40
+ expect(result).toBe(text);
41
+ });
42
+
43
+ it('should preserve emoji with skin tone modifiers', () => {
44
+ const text = '👋🏻👋🏿'; // Waving hand with different skin tones
45
+ const result = sanitizeSurrogates(text);
46
+
47
+ expect(result).toBe(text);
48
+ });
49
+ });
50
+
51
+ describe('Unpaired high surrogate removal', () => {
52
+ it('should remove unpaired high surrogate', () => {
53
+ const text = 'Hello\uD800World'; // \uD800 is unpaired high surrogate
54
+ const result = sanitizeSurrogates(text);
55
+
56
+ expect(result).toBe('HelloWorld');
57
+ expect(result).not.toContain('\uD800');
58
+ });
59
+
60
+ it('should remove multiple unpaired high surrogates', () => {
61
+ const text = '\uD800\uD800\uD800ABC';
62
+ const result = sanitizeSurrogates(text);
63
+
64
+ expect(result).toBe('ABC');
65
+ });
66
+
67
+ it('should remove unpaired high surrogate at start', () => {
68
+ const text = '\uD800Hello';
69
+ const result = sanitizeSurrogates(text);
70
+
71
+ expect(result).toBe('Hello');
72
+ });
73
+
74
+ it('should remove unpaired high surrogate at end', () => {
75
+ const text = 'Hello\uD800';
76
+ const result = sanitizeSurrogates(text);
77
+
78
+ expect(result).toBe('Hello');
79
+ });
80
+
81
+ it('should remove unpaired high surrogate in middle', () => {
82
+ const text = 'Hello\uD800World\uD800!';
83
+ const result = sanitizeSurrogates(text);
84
+
85
+ expect(result).toBe('HelloWorld!');
86
+ });
87
+ });
88
+
89
+ describe('Unpaired low surrogate removal', () => {
90
+ it('should remove unpaired low surrogate', () => {
91
+ const text = 'Hello\uDC00World'; // \uDC00 is unpaired low surrogate
92
+ const result = sanitizeSurrogates(text);
93
+
94
+ expect(result).toBe('HelloWorld');
95
+ expect(result).not.toContain('\uDC00');
96
+ });
97
+
98
+ it('should remove multiple unpaired low surrogates', () => {
99
+ const text = '\uDC00\uDC00\uDC00ABC';
100
+ const result = sanitizeSurrogates(text);
101
+
102
+ expect(result).toBe('ABC');
103
+ });
104
+
105
+ it('should remove unpaired low surrogate at start', () => {
106
+ const text = '\uDC00Hello';
107
+ const result = sanitizeSurrogates(text);
108
+
109
+ expect(result).toBe('Hello');
110
+ });
111
+
112
+ it('should remove unpaired low surrogate at end', () => {
113
+ const text = 'Hello\uDC00';
114
+ const result = sanitizeSurrogates(text);
115
+
116
+ expect(result).toBe('Hello');
117
+ });
118
+ });
119
+
120
+ describe('Mixed valid and invalid surrogates', () => {
121
+ it('should remove unpaired surrogates but keep valid emoji', () => {
122
+ const text = 'Hello\uD800😀\uDC00World';
123
+ const result = sanitizeSurrogates(text);
124
+
125
+ expect(result).toBe('Hello😀World');
126
+ expect(result).toContain('😀');
127
+ });
128
+
129
+ it('should handle text with both valid and invalid surrogates', () => {
130
+ const text = '\uD800Valid text 👋 with emoji\uDC00 and surrogates\uD801';
131
+ const result = sanitizeSurrogates(text);
132
+
133
+ expect(result).toContain('Valid text');
134
+ expect(result).toContain('👋');
135
+ expect(result).toContain('with emoji');
136
+ expect(result).not.toContain('\uD800');
137
+ expect(result).not.toContain('\uDC00');
138
+ expect(result).not.toContain('\uD801');
139
+ });
140
+
141
+ it('should preserve properly paired surrogates (emoji)', () => {
142
+ // Emoji are made of surrogate pairs, should be preserved
143
+ const text = 'Test\uD800 😊 \uDC00Text'; // Unpaired + emoji + unpaired
144
+ const result = sanitizeSurrogates(text);
145
+
146
+ expect(result).toContain('😊');
147
+ expect(result).not.toContain('\uD800');
148
+ expect(result).not.toContain('\uDC00');
149
+ });
150
+ });
151
+
152
+ describe('Empty and edge cases', () => {
153
+ it('should handle empty string', () => {
154
+ const text = '';
155
+ const result = sanitizeSurrogates(text);
156
+
157
+ expect(result).toBe('');
158
+ });
159
+
160
+ it('should handle string with only surrogates', () => {
161
+ const text = '\uD800\uDC00\uD801\uDC01';
162
+ const result = sanitizeSurrogates(text);
163
+
164
+ // May remove all or preserve pairs
165
+ expect(result).toBeDefined();
166
+ });
167
+
168
+ it('should handle single unpaired high surrogate', () => {
169
+ const text = '\uD800';
170
+ const result = sanitizeSurrogates(text);
171
+
172
+ expect(result).toBe('');
173
+ });
174
+
175
+ it('should handle single unpaired low surrogate', () => {
176
+ const text = '\uDC00';
177
+ const result = sanitizeSurrogates(text);
178
+
179
+ expect(result).toBe('');
180
+ });
181
+
182
+ it('should handle whitespace', () => {
183
+ const text = ' \n\t ';
184
+ const result = sanitizeSurrogates(text);
185
+
186
+ expect(result).toBe(text);
187
+ });
188
+ });
189
+
190
+ describe('Special characters', () => {
191
+ it('should preserve newlines and tabs', () => {
192
+ const text = 'Hello\nWorld\tTest';
193
+ const result = sanitizeSurrogates(text);
194
+
195
+ expect(result).toBe(text);
196
+ });
197
+
198
+ it('should preserve special ASCII characters', () => {
199
+ const text = '!@#$%^&*()_+-=[]{}|;:,.<>?/~`';
200
+ const result = sanitizeSurrogates(text);
201
+
202
+ expect(result).toBe(text);
203
+ });
204
+
205
+ it('should preserve quotes and apostrophes', () => {
206
+ const text = '"Hello" \'World\'';
207
+ const result = sanitizeSurrogates(text);
208
+
209
+ expect(result).toBe(text);
210
+ });
211
+ });
212
+
213
+ describe('Long strings', () => {
214
+ it('should handle long string with surrogates', () => {
215
+ const longText = 'A'.repeat(1000) + '\uD800' + 'B'.repeat(1000);
216
+ const result = sanitizeSurrogates(longText);
217
+
218
+ expect(result).toHaveLength(2000);
219
+ expect(result).not.toContain('\uD800');
220
+ });
221
+
222
+ it('should handle long string with multiple surrogates', () => {
223
+ let text = '';
224
+ for (let i = 0; i < 100; i++) {
225
+ text += 'Valid' + '\uD800' + 'Text' + '\uDC00';
226
+ }
227
+ const result = sanitizeSurrogates(text);
228
+
229
+ expect(result).not.toContain('\uD800');
230
+ expect(result).not.toContain('\uDC00');
231
+ expect(result).toContain('ValidText');
232
+ });
233
+ });
234
+
235
+ describe('Real-world scenarios', () => {
236
+ it('should clean LLM output with occasional surrogates', () => {
237
+ const llmOutput = 'The answer is 42.\uD800 This text should be clean.';
238
+ const result = sanitizeSurrogates(llmOutput);
239
+
240
+ expect(result).toBe('The answer is 42. This text should be clean.');
241
+ });
242
+
243
+ it('should handle JSON-like strings with surrogates', () => {
244
+ const jsonLike = '{"text": "Hello\uD800World"}';
245
+ const result = sanitizeSurrogates(jsonLike);
246
+
247
+ expect(result).toBe('{"text": "HelloWorld"}');
248
+ });
249
+
250
+ it('should preserve valid emoji in user messages', () => {
251
+ const userMessage = 'Thanks! 🙏 This helped a lot 😊';
252
+ const result = sanitizeSurrogates(userMessage);
253
+
254
+ expect(result).toBe(userMessage);
255
+ expect(result).toContain('🙏');
256
+ expect(result).toContain('😊');
257
+ });
258
+
259
+ it('should clean malformed API responses', () => {
260
+ const malformed = 'Error\uD800:\uDC00 Invalid request';
261
+ const result = sanitizeSurrogates(malformed);
262
+
263
+ expect(result).toBe('Error: Invalid request');
264
+ });
265
+ });
266
+
267
+ describe('Unicode normalization', () => {
268
+ it('should handle combining characters', () => {
269
+ const text = 'café'; // e + combining acute accent
270
+ const result = sanitizeSurrogates(text);
271
+
272
+ expect(result).toBeDefined();
273
+ expect(result).toContain('café');
274
+ });
275
+
276
+ it('should handle zero-width joiners (emoji)', () => {
277
+ const text = '👨‍💻'; // Man + ZWJ + Laptop
278
+ const result = sanitizeSurrogates(text);
279
+
280
+ expect(result).toBe(text);
281
+ });
282
+ });
283
+
284
+ describe('Different surrogate ranges', () => {
285
+ it('should remove high surrogates at start of range', () => {
286
+ const text = 'Test\uD800Text';
287
+ const result = sanitizeSurrogates(text);
288
+
289
+ expect(result).toBe('TestText');
290
+ });
291
+
292
+ it('should remove high surrogates at end of range', () => {
293
+ const text = 'Test\uDBFFText';
294
+ const result = sanitizeSurrogates(text);
295
+
296
+ expect(result).toBe('TestText');
297
+ });
298
+
299
+ it('should remove low surrogates at start of range', () => {
300
+ const text = 'Test\uDC00Text';
301
+ const result = sanitizeSurrogates(text);
302
+
303
+ expect(result).toBe('TestText');
304
+ });
305
+
306
+ it('should remove low surrogates at end of range', () => {
307
+ const text = 'Test\uDFFFText';
308
+ const result = sanitizeSurrogates(text);
309
+
310
+ expect(result).toBe('TestText');
311
+ });
312
+ });
313
+
314
+ describe('System message use case', () => {
315
+ it('should clean system prompts with surrogates', () => {
316
+ const systemPrompt = 'You are a helpful assistant.\uD800 Be concise.';
317
+ const result = sanitizeSurrogates(systemPrompt);
318
+
319
+ expect(result).toBe('You are a helpful assistant. Be concise.');
320
+ });
321
+
322
+ it('should preserve emoji in system prompts', () => {
323
+ const systemPrompt = 'You are a friendly 🤖 assistant. Always be helpful ✨';
324
+ const result = sanitizeSurrogates(systemPrompt);
325
+
326
+ expect(result).toBe(systemPrompt);
327
+ });
328
+ });
329
+ });