diff --git a/modules/datamodels/datamodelAi.py b/modules/datamodels/datamodelAi.py index 5fb43f79..69d51871 100644 --- a/modules/datamodels/datamodelAi.py +++ b/modules/datamodels/datamodelAi.py @@ -278,11 +278,13 @@ class JsonContinuationContexts(BaseModel): - hierarchyContext: Full structure from root to cut WITHOUT budget limitations (for internal use) - hierarchyContextForPrompt: Full structure from root to cut WITH budget limitations (for prompts) - completePart: Valid JSON with all structures properly closed + - jsonParsingSuccess: True if completePart is valid parseable JSON """ overlapContext: str = Field(description="The innermost object/array element containing the cut point (for merging)") hierarchyContext: str = Field(description="Full structure from root to cut WITHOUT budget limitations (for internal use)") hierarchyContextForPrompt: str = Field(description="Full structure from root to cut WITH budget limitations (for prompts)") completePart: str = Field(description="Valid JSON with all structures properly closed") + jsonParsingSuccess: bool = Field(default=False, description="True if completePart is valid parseable JSON") class SectionPromptArgs(BaseModel): diff --git a/modules/services/serviceAi/merge_1.txt b/modules/services/serviceAi/merge_1.txt deleted file mode 100644 index 3353c028..00000000 --- a/modules/services/serviceAi/merge_1.txt +++ /dev/null @@ -1,901 +0,0 @@ -================================================================================ -JSON MERGE OPERATION #1 -================================================================================ -Timestamp: 2026-01-05T22:51:14.407688 - -INPUT: - Accumulated length: 31737 chars - New Fragment length: 32830 chars - Accumulated: 409 lines (showing first 5 and last 5) - { - "elements": [ - { - "type": "table", - "content": { - ... (399 lines omitted) ... - [37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579], - [37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, 37691], - [37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831], - [37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957], - [37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039 - New Fragment: 406 lines (showing first 5 and last 5) - ```json - { - "elements": [ - { - "type": "table", - ... (396 lines omitted) ... - [79873, 79889, 79901, 79903, 79907, 79939, 79943, 79967, 79973, 79979], - [79987, 79997, 79999, 80021, 80039, 80051, 80071, 80077, 80107, 80111], - [80141, 80147, 80149, 80153, 80167, 80173, 80177, 80191, 80207, 80209], - [80221, 80231, 80233, 80239, 80251, 80263, 80273, 80279, 80287, 80309], - [80317, 80329, 80341, 80347, 80363, 80369, - - - Normalized Accumulated (31737 chars) - (showing first 5 and last 5 of 409 lines) - { - "elements": [ - { - "type": "table", - "content": { - ... (399 lines omitted) ... - [37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579], - [37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, 37691], - [37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831], - [37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957], - [37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039 - - Normalized New Fragment (32822 chars) - (showing first 5 and last 5 of 405 lines) - { - "elements": [ - { - "type": "table", - "content": { - ... (395 lines omitted) ... - [79873, 79889, 79901, 79903, 79907, 79939, 79943, 79967, 79973, 79979], - [79987, 79997, 79999, 80021, 80039, 80051, 80071, 80077, 80107, 80111], - [80141, 80147, 80149, 80153, 80167, 80173, 80177, 80191, 80207, 80209], - [80221, 80231, 80233, 80239, 80251, 80263, 80273, 80279, 80287, 80309], - [80317, 80329, 80341, 80347, 80363, 80369, -STEP: PHASE 1 - Description: Finding overlap between JSON strings - ⏳ In progress... - - Overlap Detection (string): - Overlap length: 0 - ⚠️ No overlap detected - appending all - - ⚠️ NO OVERLAP FOUND - This indicates iterations should stop - Closing JSON and returning final result - - Closed JSON (31743 chars): - ============================================================================== - { - "elements": [ - { - "type": "table", - "content": { - "headers": ["Spalte1", "Spalte2", "Spalte3", "Spalte4", "Spalte5", "Spalte6", "Spalte7", "Spalte8", "Spalte9", "Spalte10"], - "rows": [ - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29], - [31, 37, 41, 43, 47, 53, 59, 61, 67, 71], - [73, 79, 83, 89, 97, 101, 103, 107, 109, 113], - [127, 131, 137, 139, 149, 151, 157, 163, 167, 173], - [179, 181, 191, 193, 197, 199, 211, 223, 227, 229], - [233, 239, 241, 251, 257, 263, 269, 271, 277, 281], - [283, 293, 307, 311, 313, 317, 331, 337, 347, 349], - [353, 359, 367, 373, 379, 383, 389, 397, 401, 409], - [419, 421, 431, 433, 439, 443, 449, 457, 461, 463], - [467, 479, 487, 491, 499, 503, 509, 521, 523, 541], - [547, 557, 563, 569, 571, 577, 587, 593, 599, 601], - [607, 613, 617, 619, 631, 641, 643, 647, 653, 659], - [661, 673, 677, 683, 691, 701, 709, 719, 727, 733], - [739, 743, 751, 757, 761, 769, 773, 787, 797, 809], - [811, 821, 823, 827, 829, 839, 853, 857, 859, 863], - [877, 881, 883, 887, 907, 911, 919, 929, 937, 941], - [947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013], - [1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069], - [1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151], - [1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223], - [1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291], - [1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373], - [1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451], - [1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511], - [1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583], - [1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657], - [1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733], - [1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811], - [1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889], - [1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987], - [1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053], - [2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129], - [2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213], - [2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287], - [2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357], - [2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423], - [2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531], - [2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617], - [2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687], - [2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741], - [2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819], - [2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903], - [2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999], - [3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079], - [3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181], - [3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257], - [3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331], - [3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413], - [3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511], - [3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571], - [3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643], - [3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727], - [3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821], - [3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907], - [3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989], - [4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057], - [4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139], - [4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231], - [4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297], - [4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409], - [4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493], - [4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583], - [4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657], - [4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751], - [4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831], - [4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937], - [4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003], - [5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087], - [5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179], - [5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279], - [5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387], - [5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443], - [5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521], - [5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639], - [5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693], - [5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791], - [5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857], - [5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939], - [5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053], - [6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133], - [6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221], - [6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301], - [6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367], - [6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473], - [6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571], - [6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673], - [6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761], - [6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833], - [6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917], - [6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997], - [7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103], - [7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207], - [7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297], - [7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411], - [7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499], - [7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561], - [7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643], - [7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723], - [7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829], - [7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919], - [7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017], - [8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111], - [8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219], - [8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291], - [8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387], - [8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501], - [8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597], - [8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677], - [8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741], - [8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831], - [8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929], - [8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011], - [9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109], - [9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199], - [9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283], - [9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377], - [9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439], - [9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533], - [9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631], - [9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733], - [9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811], - [9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887], - [9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007], - [10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099], - [10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177], - [10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271], - [10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343], - [10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459], - [10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567], - [10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657], - [10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739], - [10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859], - [10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949], - [10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059], - [11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149], - [11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251], - [11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329], - [11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443], - [11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527], - [11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657], - [11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777], - [11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833], - [11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933], - [11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011], - [12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109], - [12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211], - [12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289], - [12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401], - [12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487], - [12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553], - [12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641], - [12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739], - [12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829], - [12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923], - [12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007], - [13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109], - [13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187], - [13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309], - [13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411], - [13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499], - [13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619], - [13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697], - [13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781], - [13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879], - [13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967], - [13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081], - [14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197], - [14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323], - [14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419], - [14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519], - [14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593], - [14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699], - [14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767], - [14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851], - [14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947], - [14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073], - [15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149], - [15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259], - [15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319], - [15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401], - [15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497], - [15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607], - [15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679], - [15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773], - [15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881], - [15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971], - [15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069], - [16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183], - [16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267], - [16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381], - [16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481], - [16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603], - [16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691], - [16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811], - [16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903], - [16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993], - [17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093], - [17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191], - [17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317], - [17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389], - [17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477], - [17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573], - [17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669], - [17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783], - [17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, 17891], - [17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971], - [17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059], - [18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143], - [18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233], - [18251, 18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313], - [18329, 18341, 18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427], - [18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517], - [18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637], - [18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749], - [18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, 18869, 18899], - [18911, 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009], - [19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087, 19121], - [19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213, 19219], - [19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319], - [19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423], - [19427, 19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477], - [19483, 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571], - [19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, 19699], - [19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793], - [19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891], - [19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991], - [19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051, 20063, 20071], - [20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143, 20147, 20149], - [20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249, 20261], - [20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357], - [20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443], - [20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551], - [20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693], - [20707, 20717, 20719, 20731, 20743, 20747, 20749, 20753, 20759, 20771], - [20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887, 20897], - [20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, 20981, 20983], - [21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067], - [21089, 21101, 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169], - [21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277], - [21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383], - [21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491], - [21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563], - [21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647], - [21649, 21661, 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751], - [21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821, 21839, 21841], - [21851, 21859, 21863, 21871, 21881, 21893, 21911, 21929, 21937, 21943], - [21961, 21977, 21991, 21997, 22003, 22013, 22027, 22031, 22037, 22039], - [22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123], - [22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229], - [22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307], - [22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441], - [22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543], - [22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643], - [22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727], - [22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817], - [22853, 22859, 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943], - [22961, 22963, 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029], - [23039, 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099], - [23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, 23203], - [23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, 23311, 23321], - [23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447], - [23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561], - [23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629], - [23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743], - [23747, 23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827], - [23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909], - [23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007], - [24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091], - [24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, 24151, 24169], - [24179, 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281], - [24317, 24329, 24337, 24359, 24371, 24373, 24379, 24391, 24407, 24413], - [24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, 24509, 24517], - [24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659], - [24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767], - [24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877], - [24889, 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977], - [24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097], - [25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183], - [25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303], - [25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391], - [25409, 25411, 25423, 25439, 25447, 25453, 25457, 25463, 25469, 25471], - [25523, 25537, 25541, 25561, 25577, 25579, 25583, 25589, 25601, 25603], - [25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673, 25679, 25693], - [25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799], - [25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913], - [25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999], - [26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111], - [26113, 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203], - [26209, 26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293, 26297], - [26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, 26393, 26399], - [26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497], - [26501, 26513, 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633], - [26641, 26647, 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711], - [26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801], - [26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, 26891], - [26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987], - [26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077], - [27091, 27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211], - [27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329], - [27337, 27361, 27367, 27397, 27407, 27409, 27427, 27431, 27437, 27449], - [27457, 27479, 27481, 27487, 27509, 27527, 27529, 27539, 27541, 27551], - [27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691], - [27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767], - [27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827], - [27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947], - [27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031, 28051], - [28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151], - [28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283], - [28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403], - [28409, 28411, 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499], - [28513, 28517, 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579], - [28591, 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649], - [28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, 28729], - [28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, 28817, 28837], - [28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933], - [28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059], - [29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153, 29167], - [29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251], - [29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363], - [29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443], - [29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573], - [29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671], - [29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819], - [29833, 29837, 29851, 29863, 29867, 29873, 29879, 29881, 29917, 29921], - [29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047, 30059], - [30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, 30133, 30137], - [30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241], - [30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341], - [30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469], - [30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559], - [30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689], - [30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803], - [30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871], - [30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983], - [31013, 31019, 31033, 31039, 31051, 31063, 31069, 31079, 31081, 31091], - [31121, 31123, 31139, 31147, 31151, 31153, 31159, 31177, 31181, 31189], - [31193, 31219, 31223, 31231, 31237, 31247, 31249, 31253, 31259, 31267], - [31271, 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, 31379], - [31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, 31513], - [31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607], - [31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, 31723], - [31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, 31847], - [31849, 31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, 31981], - [31991, 32003, 32009, 32027, 32029, 32051, 32057, 32059, 32063, 32069], - [32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, 32173], - [32183, 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261], - [32297, 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, 32359], - [32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, 32429], - [32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, 32531, 32533], - [32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, 32611], - [32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, 32719], - [32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833], - [32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, 32941], - [32957, 32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, 33029], - [33037, 33049, 33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119], - [33149, 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, 33223], - [33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, 33347], - [33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457], - [33461, 33469, 33479, 33487, 33493, 33503, 33521, 33529, 33533, 33547], - [33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613, 33617], - [33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703, 33713, 33721], - [33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797, 33809], - [33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, 33911], - [33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033], - [34039, 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, 34159], - [34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, 34267], - [34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, 34337, 34351], - [34361, 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, 34469], - [34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, 34543], - [34549, 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, 34651], - [34667, 34673, 34679, 34687, 34693, 34703, 34721, 34729, 34739, 34747], - [34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843, 34847, 34849], - [34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961, 34963], - [34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089], - [35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, 35171], - [35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311], - [35317, 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, 35407], - [35419, 35423, 35437, 35447, 35449, 35461, 35491, 35507, 35509, 35521], - [35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591, 35593, 35597], - [35603, 35617, 35671, 35677, 35729, 35731, 35747, 35753, 35759, 35771], - [35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, 35869], - [35879, 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977], - [35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, 36067], - [36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, 36187], - [36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, 36293], - [36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, 36389], - [36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, 36523], - [36527, 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599], - [36607, 36629, 36637, 36643, 36653, 36671, 36677, 36683, 36691, 36697], - [36709, 36713, 36721, 36739, 36749, 36761, 36767, 36779, 36781, 36787], - [36791, 36793, 36809, 36821, 36833, 36847, 36857, 36871, 36877, 36887], - [36899, 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, 36973], - [36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, 37061], - [37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, 37199], - [37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, 37313], - [37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, 37409], - [37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, 37511], - [37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579], - [37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, 37691], - [37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831], - [37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957], - [37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039]]}}]} - ============================================================================== - -================================================================================ -MERGE RESULT: ✅ SUCCESS -================================================================================ -Final result length: 31743 chars -Final result (COMPLETE): -================================================================================ -{ - "elements": [ - { - "type": "table", - "content": { - "headers": ["Spalte1", "Spalte2", "Spalte3", "Spalte4", "Spalte5", "Spalte6", "Spalte7", "Spalte8", "Spalte9", "Spalte10"], - "rows": [ - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29], - [31, 37, 41, 43, 47, 53, 59, 61, 67, 71], - [73, 79, 83, 89, 97, 101, 103, 107, 109, 113], - [127, 131, 137, 139, 149, 151, 157, 163, 167, 173], - [179, 181, 191, 193, 197, 199, 211, 223, 227, 229], - [233, 239, 241, 251, 257, 263, 269, 271, 277, 281], - [283, 293, 307, 311, 313, 317, 331, 337, 347, 349], - [353, 359, 367, 373, 379, 383, 389, 397, 401, 409], - [419, 421, 431, 433, 439, 443, 449, 457, 461, 463], - [467, 479, 487, 491, 499, 503, 509, 521, 523, 541], - [547, 557, 563, 569, 571, 577, 587, 593, 599, 601], - [607, 613, 617, 619, 631, 641, 643, 647, 653, 659], - [661, 673, 677, 683, 691, 701, 709, 719, 727, 733], - [739, 743, 751, 757, 761, 769, 773, 787, 797, 809], - [811, 821, 823, 827, 829, 839, 853, 857, 859, 863], - [877, 881, 883, 887, 907, 911, 919, 929, 937, 941], - [947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013], - [1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069], - [1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151], - [1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223], - [1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291], - [1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373], - [1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451], - [1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511], - [1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583], - [1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657], - [1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733], - [1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811], - [1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889], - [1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987], - [1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053], - [2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129], - [2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213], - [2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287], - [2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357], - [2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423], - [2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531], - [2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617], - [2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687], - [2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741], - [2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819], - [2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903], - [2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999], - [3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079], - [3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181], - [3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257], - [3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331], - [3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413], - [3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511], - [3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571], - [3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643], - [3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727], - [3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821], - [3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907], - [3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989], - [4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057], - [4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139], - [4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231], - [4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297], - [4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409], - [4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493], - [4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583], - [4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657], - [4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751], - [4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831], - [4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937], - [4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003], - [5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087], - [5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179], - [5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279], - [5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387], - [5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443], - [5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521], - [5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639], - [5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693], - [5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791], - [5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857], - [5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939], - [5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053], - [6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133], - [6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221], - [6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301], - [6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367], - [6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473], - [6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571], - [6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673], - [6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761], - [6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833], - [6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917], - [6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997], - [7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103], - [7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207], - [7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297], - [7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411], - [7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499], - [7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561], - [7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643], - [7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723], - [7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829], - [7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919], - [7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017], - [8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111], - [8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219], - [8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291], - [8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387], - [8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501], - [8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597], - [8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677], - [8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741], - [8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831], - [8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929], - [8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011], - [9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109], - [9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199], - [9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283], - [9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377], - [9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439], - [9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533], - [9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631], - [9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733], - [9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811], - [9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887], - [9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007], - [10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099], - [10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177], - [10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271], - [10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343], - [10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459], - [10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567], - [10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657], - [10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739], - [10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859], - [10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949], - [10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059], - [11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149], - [11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251], - [11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329], - [11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443], - [11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527], - [11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657], - [11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777], - [11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833], - [11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933], - [11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011], - [12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109], - [12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211], - [12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289], - [12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401], - [12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487], - [12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553], - [12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641], - [12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739], - [12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829], - [12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923], - [12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007], - [13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109], - [13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187], - [13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309], - [13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411], - [13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499], - [13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619], - [13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697], - [13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781], - [13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879], - [13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967], - [13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081], - [14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197], - [14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323], - [14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419], - [14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519], - [14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593], - [14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699], - [14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767], - [14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851], - [14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947], - [14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073], - [15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149], - [15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259], - [15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319], - [15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401], - [15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497], - [15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607], - [15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679], - [15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773], - [15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881], - [15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971], - [15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069], - [16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183], - [16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267], - [16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381], - [16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481], - [16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603], - [16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691], - [16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811], - [16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903], - [16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993], - [17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093], - [17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191], - [17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317], - [17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389], - [17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477], - [17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573], - [17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669], - [17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783], - [17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, 17891], - [17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971], - [17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059], - [18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143], - [18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233], - [18251, 18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313], - [18329, 18341, 18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427], - [18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517], - [18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637], - [18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749], - [18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, 18869, 18899], - [18911, 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009], - [19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087, 19121], - [19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213, 19219], - [19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319], - [19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423], - [19427, 19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477], - [19483, 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571], - [19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, 19699], - [19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793], - [19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891], - [19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991], - [19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051, 20063, 20071], - [20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143, 20147, 20149], - [20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249, 20261], - [20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357], - [20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443], - [20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551], - [20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693], - [20707, 20717, 20719, 20731, 20743, 20747, 20749, 20753, 20759, 20771], - [20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887, 20897], - [20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, 20981, 20983], - [21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067], - [21089, 21101, 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169], - [21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277], - [21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383], - [21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491], - [21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563], - [21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647], - [21649, 21661, 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751], - [21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821, 21839, 21841], - [21851, 21859, 21863, 21871, 21881, 21893, 21911, 21929, 21937, 21943], - [21961, 21977, 21991, 21997, 22003, 22013, 22027, 22031, 22037, 22039], - [22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123], - [22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229], - [22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307], - [22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441], - [22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543], - [22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643], - [22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727], - [22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817], - [22853, 22859, 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943], - [22961, 22963, 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029], - [23039, 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099], - [23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, 23203], - [23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, 23311, 23321], - [23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447], - [23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561], - [23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629], - [23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743], - [23747, 23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827], - [23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909], - [23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007], - [24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091], - [24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, 24151, 24169], - [24179, 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281], - [24317, 24329, 24337, 24359, 24371, 24373, 24379, 24391, 24407, 24413], - [24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, 24509, 24517], - [24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659], - [24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767], - [24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877], - [24889, 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977], - [24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097], - [25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183], - [25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303], - [25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391], - [25409, 25411, 25423, 25439, 25447, 25453, 25457, 25463, 25469, 25471], - [25523, 25537, 25541, 25561, 25577, 25579, 25583, 25589, 25601, 25603], - [25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673, 25679, 25693], - [25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799], - [25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913], - [25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999], - [26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111], - [26113, 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203], - [26209, 26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293, 26297], - [26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, 26393, 26399], - [26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497], - [26501, 26513, 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633], - [26641, 26647, 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711], - [26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801], - [26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, 26891], - [26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987], - [26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077], - [27091, 27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211], - [27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329], - [27337, 27361, 27367, 27397, 27407, 27409, 27427, 27431, 27437, 27449], - [27457, 27479, 27481, 27487, 27509, 27527, 27529, 27539, 27541, 27551], - [27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691], - [27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767], - [27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827], - [27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947], - [27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031, 28051], - [28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151], - [28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283], - [28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403], - [28409, 28411, 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499], - [28513, 28517, 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579], - [28591, 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649], - [28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, 28729], - [28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, 28817, 28837], - [28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933], - [28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059], - [29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153, 29167], - [29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251], - [29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363], - [29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443], - [29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573], - [29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671], - [29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819], - [29833, 29837, 29851, 29863, 29867, 29873, 29879, 29881, 29917, 29921], - [29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047, 30059], - [30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, 30133, 30137], - [30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241], - [30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341], - [30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469], - [30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559], - [30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689], - [30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803], - [30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871], - [30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983], - [31013, 31019, 31033, 31039, 31051, 31063, 31069, 31079, 31081, 31091], - [31121, 31123, 31139, 31147, 31151, 31153, 31159, 31177, 31181, 31189], - [31193, 31219, 31223, 31231, 31237, 31247, 31249, 31253, 31259, 31267], - [31271, 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, 31379], - [31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, 31513], - [31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607], - [31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, 31723], - [31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, 31847], - [31849, 31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, 31981], - [31991, 32003, 32009, 32027, 32029, 32051, 32057, 32059, 32063, 32069], - [32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, 32173], - [32183, 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261], - [32297, 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, 32359], - [32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, 32429], - [32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, 32531, 32533], - [32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, 32611], - [32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, 32719], - [32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833], - [32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, 32941], - [32957, 32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, 33029], - [33037, 33049, 33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119], - [33149, 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, 33223], - [33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, 33347], - [33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457], - [33461, 33469, 33479, 33487, 33493, 33503, 33521, 33529, 33533, 33547], - [33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613, 33617], - [33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703, 33713, 33721], - [33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797, 33809], - [33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, 33911], - [33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033], - [34039, 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, 34159], - [34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, 34267], - [34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, 34337, 34351], - [34361, 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, 34469], - [34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, 34543], - [34549, 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, 34651], - [34667, 34673, 34679, 34687, 34693, 34703, 34721, 34729, 34739, 34747], - [34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843, 34847, 34849], - [34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961, 34963], - [34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089], - [35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, 35171], - [35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311], - [35317, 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, 35407], - [35419, 35423, 35437, 35447, 35449, 35461, 35491, 35507, 35509, 35521], - [35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591, 35593, 35597], - [35603, 35617, 35671, 35677, 35729, 35731, 35747, 35753, 35759, 35771], - [35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, 35869], - [35879, 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977], - [35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, 36067], - [36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, 36187], - [36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, 36293], - [36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, 36389], - [36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, 36523], - [36527, 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599], - [36607, 36629, 36637, 36643, 36653, 36671, 36677, 36683, 36691, 36697], - [36709, 36713, 36721, 36739, 36749, 36761, 36767, 36779, 36781, 36787], - [36791, 36793, 36809, 36821, 36833, 36847, 36857, 36871, 36877, 36887], - [36899, 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, 36973], - [36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, 37061], - [37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, 37199], - [37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, 37313], - [37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, 37409], - [37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, 37511], - [37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579], - [37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, 37691], - [37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831], - [37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957], - [37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039]]}}]} -================================================================================ diff --git a/modules/services/serviceAi/merge_2.txt b/modules/services/serviceAi/merge_2.txt deleted file mode 100644 index 6343dbaf..00000000 --- a/modules/services/serviceAi/merge_2.txt +++ /dev/null @@ -1,3989 +0,0 @@ -================================================================================ -JSON MERGE OPERATION #2 -================================================================================ -Timestamp: 2026-01-05T23:20:09.019590 - -INPUT: - Accumulated length: 54039 chars - New Fragment length: 52810 chars - Accumulated: 1954 lines (showing first 5 and last 5) - { - "elements": [ - { - "type": "table", - "content": { - ... (1944 lines omitted) ... - ["1943", "16831"], - ["1944", "16843"], - ["1945", "16871"], - ["1946", "16879"], - - New Fragment: 1825 lines (showing first 5 and last 5) - ```json - { - "elements": [ - { - "type": "table", - ... (1815 lines omitted) ... - ["3725", "34897"], - ["3726", "34913"], - ["3727", "34919"], - ["3728", "34939"], - - - - Normalized Accumulated (54029 chars) - (showing first 5 and last 5 of 1953 lines) - { - "elements": [ - { - "type": "table", - "content": { - ... (1943 lines omitted) ... - ["1942", "16829"], - ["1943", "16831"], - ["1944", "16843"], - ["1945", "16871"], - ["1946", "16879"], - - Normalized New Fragment (52792 chars) - (showing first 5 and last 5 of 1823 lines) - { - "elements": [ - { - "type": "table", - "content": { - ... (1813 lines omitted) ... - ["3724", "34883"], - ["3725", "34897"], - ["3726", "34913"], - ["3727", "34919"], - ["3728", "34939"], -STEP: PHASE 1 - Description: Finding overlap between JSON strings - ⏳ In progress... - - Overlap Detection (string): - Overlap length: 0 - ⚠️ No overlap detected - appending all - - ⚠️ NO OVERLAP FOUND - This indicates iterations should stop - Closing JSON and returning final result - - Closed JSON (54033 chars): - ============================================================================== - { - "elements": [ - { - "type": "table", - "content": { - "headers": ["Position", "Primzahl"], - "rows": [ - ["1", "2"], - ["2", "3"], - ["3", "5"], - ["4", "7"], - ["5", "11"], - ["6", "13"], - ["7", "17"], - ["8", "19"], - ["9", "23"], - ["10", "29"], - ["11", "31"], - ["12", "37"], - ["13", "41"], - ["14", "43"], - ["15", "47"], - ["16", "53"], - ["17", "59"], - ["18", "61"], - ["19", "67"], - ["20", "71"], - ["21", "73"], - ["22", "79"], - ["23", "83"], - ["24", "89"], - ["25", "97"], - ["26", "101"], - ["27", "103"], - ["28", "107"], - ["29", "109"], - ["30", "113"], - ["31", "127"], - ["32", "131"], - ["33", "137"], - ["34", "139"], - ["35", "149"], - ["36", "151"], - ["37", "157"], - ["38", "163"], - ["39", "167"], - ["40", "173"], - ["41", "179"], - ["42", "181"], - ["43", "191"], - ["44", "193"], - ["45", "197"], - ["46", "199"], - ["47", "211"], - ["48", "223"], - ["49", "227"], - ["50", "229"], - ["51", "233"], - ["52", "239"], - ["53", "241"], - ["54", "251"], - ["55", "257"], - ["56", "263"], - ["57", "269"], - ["58", "271"], - ["59", "277"], - ["60", "281"], - ["61", "283"], - ["62", "293"], - ["63", "307"], - ["64", "311"], - ["65", "313"], - ["66", "317"], - ["67", "331"], - ["68", "337"], - ["69", "347"], - ["70", "349"], - ["71", "353"], - ["72", "359"], - ["73", "367"], - ["74", "373"], - ["75", "379"], - ["76", "383"], - ["77", "389"], - ["78", "397"], - ["79", "401"], - ["80", "409"], - ["81", "419"], - ["82", "421"], - ["83", "431"], - ["84", "433"], - ["85", "439"], - ["86", "443"], - ["87", "449"], - ["88", "457"], - ["89", "461"], - ["90", "463"], - ["91", "467"], - ["92", "479"], - ["93", "487"], - ["94", "491"], - ["95", "499"], - ["96", "503"], - ["97", "509"], - ["98", "521"], - ["99", "523"], - ["100", "541"], - ["101", "547"], - ["102", "557"], - ["103", "563"], - ["104", "569"], - ["105", "571"], - ["106", "577"], - ["107", "587"], - ["108", "593"], - ["109", "599"], - ["110", "601"], - ["111", "607"], - ["112", "613"], - ["113", "617"], - ["114", "619"], - ["115", "631"], - ["116", "641"], - ["117", "643"], - ["118", "647"], - ["119", "653"], - ["120", "659"], - ["121", "661"], - ["122", "673"], - ["123", "677"], - ["124", "683"], - ["125", "691"], - ["126", "701"], - ["127", "709"], - ["128", "719"], - ["129", "727"], - ["130", "733"], - ["131", "739"], - ["132", "743"], - ["133", "751"], - ["134", "757"], - ["135", "761"], - ["136", "769"], - ["137", "773"], - ["138", "787"], - ["139", "797"], - ["140", "809"], - ["141", "811"], - ["142", "821"], - ["143", "823"], - ["144", "827"], - ["145", "829"], - ["146", "839"], - ["147", "853"], - ["148", "857"], - ["149", "859"], - ["150", "863"], - ["151", "877"], - ["152", "881"], - ["153", "883"], - ["154", "887"], - ["155", "907"], - ["156", "911"], - ["157", "919"], - ["158", "929"], - ["159", "937"], - ["160", "941"], - ["161", "947"], - ["162", "953"], - ["163", "967"], - ["164", "971"], - ["165", "977"], - ["166", "983"], - ["167", "991"], - ["168", "997"], - ["169", "1009"], - ["170", "1013"], - ["171", "1019"], - ["172", "1021"], - ["173", "1031"], - ["174", "1033"], - ["175", "1039"], - ["176", "1049"], - ["177", "1051"], - ["178", "1061"], - ["179", "1063"], - ["180", "1069"], - ["181", "1087"], - ["182", "1091"], - ["183", "1093"], - ["184", "1097"], - ["185", "1103"], - ["186", "1109"], - ["187", "1117"], - ["188", "1123"], - ["189", "1129"], - ["190", "1151"], - ["191", "1153"], - ["192", "1163"], - ["193", "1171"], - ["194", "1181"], - ["195", "1187"], - ["196", "1193"], - ["197", "1201"], - ["198", "1213"], - ["199", "1217"], - ["200", "1223"], - ["201", "1229"], - ["202", "1231"], - ["203", "1237"], - ["204", "1249"], - ["205", "1259"], - ["206", "1277"], - ["207", "1279"], - ["208", "1283"], - ["209", "1289"], - ["210", "1291"], - ["211", "1297"], - ["212", "1301"], - ["213", "1303"], - ["214", "1307"], - ["215", "1319"], - ["216", "1321"], - ["217", "1327"], - ["218", "1361"], - ["219", "1367"], - ["220", "1373"], - ["221", "1381"], - ["222", "1399"], - ["223", "1409"], - ["224", "1423"], - ["225", "1427"], - ["226", "1429"], - ["227", "1433"], - ["228", "1439"], - ["229", "1447"], - ["230", "1451"], - ["231", "1453"], - ["232", "1459"], - ["233", "1471"], - ["234", "1481"], - ["235", "1483"], - ["236", "1487"], - ["237", "1489"], - ["238", "1493"], - ["239", "1499"], - ["240", "1511"], - ["241", "1523"], - ["242", "1531"], - ["243", "1543"], - ["244", "1549"], - ["245", "1553"], - ["246", "1559"], - ["247", "1567"], - ["248", "1571"], - ["249", "1579"], - ["250", "1583"], - ["251", "1597"], - ["252", "1601"], - ["253", "1607"], - ["254", "1609"], - ["255", "1613"], - ["256", "1619"], - ["257", "1621"], - ["258", "1627"], - ["259", "1637"], - ["260", "1657"], - ["261", "1663"], - ["262", "1667"], - ["263", "1669"], - ["264", "1693"], - ["265", "1697"], - ["266", "1699"], - ["267", "1709"], - ["268", "1721"], - ["269", "1723"], - ["270", "1733"], - ["271", "1741"], - ["272", "1747"], - ["273", "1753"], - ["274", "1759"], - ["275", "1777"], - ["276", "1783"], - ["277", "1787"], - ["278", "1789"], - ["279", "1801"], - ["280", "1811"], - ["281", "1823"], - ["282", "1831"], - ["283", "1847"], - ["284", "1861"], - ["285", "1867"], - ["286", "1871"], - ["287", "1873"], - ["288", "1877"], - ["289", "1879"], - ["290", "1889"], - ["291", "1901"], - ["292", "1907"], - ["293", "1913"], - ["294", "1931"], - ["295", "1933"], - ["296", "1949"], - ["297", "1951"], - ["298", "1973"], - ["299", "1979"], - ["300", "1987"], - ["301", "1993"], - ["302", "1997"], - ["303", "1999"], - ["304", "2003"], - ["305", "2011"], - ["306", "2017"], - ["307", "2027"], - ["308", "2029"], - ["309", "2039"], - ["310", "2053"], - ["311", "2063"], - ["312", "2069"], - ["313", "2081"], - ["314", "2083"], - ["315", "2087"], - ["316", "2089"], - ["317", "2099"], - ["318", "2111"], - ["319", "2113"], - ["320", "2129"], - ["321", "2131"], - ["322", "2137"], - ["323", "2141"], - ["324", "2143"], - ["325", "2153"], - ["326", "2161"], - ["327", "2179"], - ["328", "2203"], - ["329", "2207"], - ["330", "2213"], - ["331", "2221"], - ["332", "2237"], - ["333", "2239"], - ["334", "2243"], - ["335", "2251"], - ["336", "2267"], - ["337", "2269"], - ["338", "2273"], - ["339", "2281"], - ["340", "2287"], - ["341", "2293"], - ["342", "2297"], - ["343", "2309"], - ["344", "2311"], - ["345", "2333"], - ["346", "2339"], - ["347", "2341"], - ["348", "2347"], - ["349", "2351"], - ["350", "2357"], - ["351", "2371"], - ["352", "2377"], - ["353", "2381"], - ["354", "2383"], - ["355", "2389"], - ["356", "2393"], - ["357", "2399"], - ["358", "2411"], - ["359", "2417"], - ["360", "2423"], - ["361", "2437"], - ["362", "2441"], - ["363", "2447"], - ["364", "2459"], - ["365", "2467"], - ["366", "2473"], - ["367", "2477"], - ["368", "2503"], - ["369", "2521"], - ["370", "2531"], - ["371", "2539"], - ["372", "2543"], - ["373", "2549"], - ["374", "2551"], - ["375", "2557"], - ["376", "2579"], - ["377", "2591"], - ["378", "2593"], - ["379", "2609"], - ["380", "2617"], - ["381", "2621"], - ["382", "2633"], - ["383", "2647"], - ["384", "2657"], - ["385", "2659"], - ["386", "2663"], - ["387", "2671"], - ["388", "2677"], - ["389", "2683"], - ["390", "2687"], - ["391", "2689"], - ["392", "2693"], - ["393", "2699"], - ["394", "2707"], - ["395", "2711"], - ["396", "2713"], - ["397", "2719"], - ["398", "2729"], - ["399", "2731"], - ["400", "2741"], - ["401", "2749"], - ["402", "2753"], - ["403", "2767"], - ["404", "2777"], - ["405", "2789"], - ["406", "2791"], - ["407", "2797"], - ["408", "2801"], - ["409", "2803"], - ["410", "2819"], - ["411", "2833"], - ["412", "2837"], - ["413", "2843"], - ["414", "2851"], - ["415", "2857"], - ["416", "2861"], - ["417", "2879"], - ["418", "2887"], - ["419", "2897"], - ["420", "2903"], - ["421", "2909"], - ["422", "2917"], - ["423", "2927"], - ["424", "2939"], - ["425", "2953"], - ["426", "2957"], - ["427", "2963"], - ["428", "2969"], - ["429", "2971"], - ["430", "2999"], - ["431", "3001"], - ["432", "3011"], - ["433", "3019"], - ["434", "3023"], - ["435", "3037"], - ["436", "3041"], - ["437", "3049"], - ["438", "3061"], - ["439", "3067"], - ["440", "3079"], - ["441", "3083"], - ["442", "3089"], - ["443", "3109"], - ["444", "3119"], - ["445", "3121"], - ["446", "3137"], - ["447", "3163"], - ["448", "3167"], - ["449", "3169"], - ["450", "3181"], - ["451", "3187"], - ["452", "3191"], - ["453", "3203"], - ["454", "3209"], - ["455", "3217"], - ["456", "3221"], - ["457", "3229"], - ["458", "3251"], - ["459", "3253"], - ["460", "3257"], - ["461", "3259"], - ["462", "3271"], - ["463", "3299"], - ["464", "3301"], - ["465", "3307"], - ["466", "3313"], - ["467", "3319"], - ["468", "3323"], - ["469", "3329"], - ["470", "3331"], - ["471", "3343"], - ["472", "3347"], - ["473", "3359"], - ["474", "3361"], - ["475", "3371"], - ["476", "3373"], - ["477", "3389"], - ["478", "3391"], - ["479", "3407"], - ["480", "3413"], - ["481", "3433"], - ["482", "3449"], - ["483", "3457"], - ["484", "3461"], - ["485", "3463"], - ["486", "3467"], - ["487", "3469"], - ["488", "3491"], - ["489", "3499"], - ["490", "3511"], - ["491", "3517"], - ["492", "3527"], - ["493", "3529"], - ["494", "3533"], - ["495", "3539"], - ["496", "3541"], - ["497", "3547"], - ["498", "3557"], - ["499", "3559"], - ["500", "3571"], - ["501", "3581"], - ["502", "3583"], - ["503", "3593"], - ["504", "3607"], - ["505", "3613"], - ["506", "3617"], - ["507", "3623"], - ["508", "3631"], - ["509", "3637"], - ["510", "3643"], - ["511", "3659"], - ["512", "3671"], - ["513", "3673"], - ["514", "3677"], - ["515", "3691"], - ["516", "3697"], - ["517", "3701"], - ["518", "3709"], - ["519", "3719"], - ["520", "3727"], - ["521", "3733"], - ["522", "3739"], - ["523", "3761"], - ["524", "3767"], - ["525", "3769"], - ["526", "3779"], - ["527", "3793"], - ["528", "3797"], - ["529", "3803"], - ["530", "3821"], - ["531", "3823"], - ["532", "3833"], - ["533", "3847"], - ["534", "3851"], - ["535", "3853"], - ["536", "3863"], - ["537", "3877"], - ["538", "3881"], - ["539", "3889"], - ["540", "3907"], - ["541", "3911"], - ["542", "3917"], - ["543", "3919"], - ["544", "3923"], - ["545", "3929"], - ["546", "3931"], - ["547", "3943"], - ["548", "3947"], - ["549", "3967"], - ["550", "3989"], - ["551", "4001"], - ["552", "4003"], - ["553", "4007"], - ["554", "4013"], - ["555", "4019"], - ["556", "4021"], - ["557", "4027"], - ["558", "4049"], - ["559", "4051"], - ["560", "4057"], - ["561", "4073"], - ["562", "4079"], - ["563", "4091"], - ["564", "4093"], - ["565", "4099"], - ["566", "4111"], - ["567", "4127"], - ["568", "4129"], - ["569", "4133"], - ["570", "4139"], - ["571", "4153"], - ["572", "4157"], - ["573", "4159"], - ["574", "4177"], - ["575", "4201"], - ["576", "4211"], - ["577", "4217"], - ["578", "4219"], - ["579", "4229"], - ["580", "4231"], - ["581", "4241"], - ["582", "4243"], - ["583", "4253"], - ["584", "4259"], - ["585", "4261"], - ["586", "4271"], - ["587", "4273"], - ["588", "4283"], - ["589", "4289"], - ["590", "4297"], - ["591", "4327"], - ["592", "4337"], - ["593", "4339"], - ["594", "4349"], - ["595", "4357"], - ["596", "4363"], - ["597", "4373"], - ["598", "4391"], - ["599", "4397"], - ["600", "4409"], - ["601", "4421"], - ["602", "4423"], - ["603", "4441"], - ["604", "4447"], - ["605", "4451"], - ["606", "4457"], - ["607", "4463"], - ["608", "4481"], - ["609", "4483"], - ["610", "4493"], - ["611", "4507"], - ["612", "4513"], - ["613", "4517"], - ["614", "4519"], - ["615", "4523"], - ["616", "4547"], - ["617", "4549"], - ["618", "4561"], - ["619", "4567"], - ["620", "4583"], - ["621", "4591"], - ["622", "4597"], - ["623", "4603"], - ["624", "4621"], - ["625", "4637"], - ["626", "4639"], - ["627", "4643"], - ["628", "4649"], - ["629", "4651"], - ["630", "4657"], - ["631", "4663"], - ["632", "4673"], - ["633", "4679"], - ["634", "4691"], - ["635", "4703"], - ["636", "4721"], - ["637", "4723"], - ["638", "4729"], - ["639", "4733"], - ["640", "4751"], - ["641", "4759"], - ["642", "4783"], - ["643", "4787"], - ["644", "4789"], - ["645", "4793"], - ["646", "4799"], - ["647", "4801"], - ["648", "4813"], - ["649", "4817"], - ["650", "4831"], - ["651", "4861"], - ["652", "4871"], - ["653", "4877"], - ["654", "4889"], - ["655", "4903"], - ["656", "4909"], - ["657", "4919"], - ["658", "4931"], - ["659", "4933"], - ["660", "4937"], - ["661", "4943"], - ["662", "4951"], - ["663", "4957"], - ["664", "4967"], - ["665", "4969"], - ["666", "4973"], - ["667", "4987"], - ["668", "4993"], - ["669", "4999"], - ["670", "5003"], - ["671", "5009"], - ["672", "5011"], - ["673", "5021"], - ["674", "5023"], - ["675", "5039"], - ["676", "5051"], - ["677", "5059"], - ["678", "5077"], - ["679", "5081"], - ["680", "5087"], - ["681", "5099"], - ["682", "5101"], - ["683", "5107"], - ["684", "5113"], - ["685", "5119"], - ["686", "5147"], - ["687", "5153"], - ["688", "5167"], - ["689", "5171"], - ["690", "5179"], - ["691", "5189"], - ["692", "5197"], - ["693", "5209"], - ["694", "5227"], - ["695", "5231"], - ["696", "5233"], - ["697", "5237"], - ["698", "5261"], - ["699", "5273"], - ["700", "5279"], - ["701", "5281"], - ["702", "5297"], - ["703", "5303"], - ["704", "5309"], - ["705", "5323"], - ["706", "5333"], - ["707", "5347"], - ["708", "5351"], - ["709", "5381"], - ["710", "5387"], - ["711", "5393"], - ["712", "5399"], - ["713", "5407"], - ["714", "5413"], - ["715", "5417"], - ["716", "5419"], - ["717", "5431"], - ["718", "5437"], - ["719", "5441"], - ["720", "5443"], - ["721", "5449"], - ["722", "5471"], - ["723", "5477"], - ["724", "5479"], - ["725", "5483"], - ["726", "5501"], - ["727", "5503"], - ["728", "5507"], - ["729", "5519"], - ["730", "5521"], - ["731", "5527"], - ["732", "5531"], - ["733", "5557"], - ["734", "5563"], - ["735", "5569"], - ["736", "5573"], - ["737", "5581"], - ["738", "5591"], - ["739", "5623"], - ["740", "5639"], - ["741", "5641"], - ["742", "5647"], - ["743", "5651"], - ["744", "5653"], - ["745", "5657"], - ["746", "5659"], - ["747", "5669"], - ["748", "5683"], - ["749", "5689"], - ["750", "5693"], - ["751", "5701"], - ["752", "5711"], - ["753", "5717"], - ["754", "5737"], - ["755", "5741"], - ["756", "5743"], - ["757", "5749"], - ["758", "5779"], - ["759", "5783"], - ["760", "5791"], - ["761", "5801"], - ["762", "5807"], - ["763", "5813"], - ["764", "5821"], - ["765", "5827"], - ["766", "5839"], - ["767", "5843"], - ["768", "5849"], - ["769", "5851"], - ["770", "5857"], - ["771", "5861"], - ["772", "5867"], - ["773", "5869"], - ["774", "5879"], - ["775", "5881"], - ["776", "5897"], - ["777", "5903"], - ["778", "5923"], - ["779", "5927"], - ["780", "5939"], - ["781", "5953"], - ["782", "5981"], - ["783", "5987"], - ["784", "6007"], - ["785", "6011"], - ["786", "6029"], - ["787", "6037"], - ["788", "6043"], - ["789", "6047"], - ["790", "6053"], - ["791", "6067"], - ["792", "6073"], - ["793", "6079"], - ["794", "6089"], - ["795", "6091"], - ["796", "6101"], - ["797", "6113"], - ["798", "6121"], - ["799", "6131"], - ["800", "6133"], - ["801", "6143"], - ["802", "6151"], - ["803", "6163"], - ["804", "6173"], - ["805", "6197"], - ["806", "6199"], - ["807", "6203"], - ["808", "6211"], - ["809", "6217"], - ["810", "6221"], - ["811", "6229"], - ["812", "6247"], - ["813", "6257"], - ["814", "6263"], - ["815", "6269"], - ["816", "6271"], - ["817", "6277"], - ["818", "6287"], - ["819", "6299"], - ["820", "6301"], - ["821", "6311"], - ["822", "6317"], - ["823", "6323"], - ["824", "6329"], - ["825", "6337"], - ["826", "6343"], - ["827", "6353"], - ["828", "6359"], - ["829", "6361"], - ["830", "6367"], - ["831", "6373"], - ["832", "6379"], - ["833", "6389"], - ["834", "6397"], - ["835", "6421"], - ["836", "6427"], - ["837", "6449"], - ["838", "6451"], - ["839", "6469"], - ["840", "6473"], - ["841", "6481"], - ["842", "6491"], - ["843", "6521"], - ["844", "6529"], - ["845", "6547"], - ["846", "6551"], - ["847", "6553"], - ["848", "6563"], - ["849", "6569"], - ["850", "6571"], - ["851", "6577"], - ["852", "6581"], - ["853", "6599"], - ["854", "6607"], - ["855", "6619"], - ["856", "6637"], - ["857", "6653"], - ["858", "6659"], - ["859", "6661"], - ["860", "6673"], - ["861", "6679"], - ["862", "6689"], - ["863", "6691"], - ["864", "6701"], - ["865", "6703"], - ["866", "6709"], - ["867", "6719"], - ["868", "6733"], - ["869", "6737"], - ["870", "6761"], - ["871", "6763"], - ["872", "6779"], - ["873", "6781"], - ["874", "6791"], - ["875", "6793"], - ["876", "6803"], - ["877", "6823"], - ["878", "6827"], - ["879", "6829"], - ["880", "6833"], - ["881", "6841"], - ["882", "6857"], - ["883", "6863"], - ["884", "6869"], - ["885", "6871"], - ["886", "6883"], - ["887", "6899"], - ["888", "6907"], - ["889", "6911"], - ["890", "6917"], - ["891", "6947"], - ["892", "6949"], - ["893", "6959"], - ["894", "6961"], - ["895", "6967"], - ["896", "6971"], - ["897", "6977"], - ["898", "6983"], - ["899", "6991"], - ["900", "6997"], - ["901", "7001"], - ["902", "7013"], - ["903", "7019"], - ["904", "7027"], - ["905", "7039"], - ["906", "7043"], - ["907", "7057"], - ["908", "7069"], - ["909", "7079"], - ["910", "7103"], - ["911", "7109"], - ["912", "7121"], - ["913", "7127"], - ["914", "7129"], - ["915", "7151"], - ["916", "7159"], - ["917", "7177"], - ["918", "7187"], - ["919", "7193"], - ["920", "7207"], - ["921", "7211"], - ["922", "7213"], - ["923", "7219"], - ["924", "7229"], - ["925", "7237"], - ["926", "7243"], - ["927", "7247"], - ["928", "7253"], - ["929", "7283"], - ["930", "7297"], - ["931", "7307"], - ["932", "7309"], - ["933", "7321"], - ["934", "7331"], - ["935", "7333"], - ["936", "7349"], - ["937", "7351"], - ["938", "7369"], - ["939", "7393"], - ["940", "7411"], - ["941", "7417"], - ["942", "7433"], - ["943", "7451"], - ["944", "7457"], - ["945", "7459"], - ["946", "7477"], - ["947", "7481"], - ["948", "7487"], - ["949", "7489"], - ["950", "7499"], - ["951", "7507"], - ["952", "7517"], - ["953", "7523"], - ["954", "7529"], - ["955", "7537"], - ["956", "7541"], - ["957", "7547"], - ["958", "7549"], - ["959", "7559"], - ["960", "7561"], - ["961", "7573"], - ["962", "7577"], - ["963", "7583"], - ["964", "7589"], - ["965", "7591"], - ["966", "7603"], - ["967", "7607"], - ["968", "7621"], - ["969", "7639"], - ["970", "7643"], - ["971", "7649"], - ["972", "7669"], - ["973", "7673"], - ["974", "7681"], - ["975", "7687"], - ["976", "7691"], - ["977", "7699"], - ["978", "7703"], - ["979", "7717"], - ["980", "7723"], - ["981", "7727"], - ["982", "7741"], - ["983", "7753"], - ["984", "7757"], - ["985", "7759"], - ["986", "7789"], - ["987", "7793"], - ["988", "7817"], - ["989", "7823"], - ["990", "7829"], - ["991", "7841"], - ["992", "7853"], - ["993", "7867"], - ["994", "7873"], - ["995", "7877"], - ["996", "7879"], - ["997", "7883"], - ["998", "7901"], - ["999", "7907"], - ["1000", "7919"], - ["1001", "7927"], - ["1002", "7933"], - ["1003", "7937"], - ["1004", "7949"], - ["1005", "7951"], - ["1006", "7963"], - ["1007", "7993"], - ["1008", "8009"], - ["1009", "8011"], - ["1010", "8017"], - ["1011", "8039"], - ["1012", "8053"], - ["1013", "8059"], - ["1014", "8069"], - ["1015", "8081"], - ["1016", "8087"], - ["1017", "8089"], - ["1018", "8093"], - ["1019", "8101"], - ["1020", "8111"], - ["1021", "8117"], - ["1022", "8123"], - ["1023", "8147"], - ["1024", "8161"], - ["1025", "8167"], - ["1026", "8171"], - ["1027", "8179"], - ["1028", "8191"], - ["1029", "8209"], - ["1030", "8219"], - ["1031", "8221"], - ["1032", "8231"], - ["1033", "8233"], - ["1034", "8237"], - ["1035", "8243"], - ["1036", "8263"], - ["1037", "8269"], - ["1038", "8273"], - ["1039", "8287"], - ["1040", "8291"], - ["1041", "8293"], - ["1042", "8297"], - ["1043", "8311"], - ["1044", "8317"], - ["1045", "8329"], - ["1046", "8353"], - ["1047", "8363"], - ["1048", "8369"], - ["1049", "8377"], - ["1050", "8387"], - ["1051", "8389"], - ["1052", "8419"], - ["1053", "8423"], - ["1054", "8429"], - ["1055", "8431"], - ["1056", "8443"], - ["1057", "8447"], - ["1058", "8461"], - ["1059", "8467"], - ["1060", "8501"], - ["1061", "8513"], - ["1062", "8521"], - ["1063", "8527"], - ["1064", "8537"], - ["1065", "8539"], - ["1066", "8543"], - ["1067", "8563"], - ["1068", "8573"], - ["1069", "8581"], - ["1070", "8597"], - ["1071", "8599"], - ["1072", "8609"], - ["1073", "8623"], - ["1074", "8627"], - ["1075", "8629"], - ["1076", "8641"], - ["1077", "8647"], - ["1078", "8663"], - ["1079", "8669"], - ["1080", "8677"], - ["1081", "8681"], - ["1082", "8689"], - ["1083", "8693"], - ["1084", "8699"], - ["1085", "8707"], - ["1086", "8713"], - ["1087", "8719"], - ["1088", "8731"], - ["1089", "8737"], - ["1090", "8741"], - ["1091", "8747"], - ["1092", "8753"], - ["1093", "8761"], - ["1094", "8779"], - ["1095", "8783"], - ["1096", "8803"], - ["1097", "8807"], - ["1098", "8819"], - ["1099", "8821"], - ["1100", "8831"], - ["1101", "8837"], - ["1102", "8839"], - ["1103", "8849"], - ["1104", "8861"], - ["1105", "8863"], - ["1106", "8867"], - ["1107", "8887"], - ["1108", "8893"], - ["1109", "8923"], - ["1110", "8929"], - ["1111", "8933"], - ["1112", "8941"], - ["1113", "8951"], - ["1114", "8963"], - ["1115", "8969"], - ["1116", "8971"], - ["1117", "8999"], - ["1118", "9001"], - ["1119", "9007"], - ["1120", "9011"], - ["1121", "9013"], - ["1122", "9029"], - ["1123", "9041"], - ["1124", "9043"], - ["1125", "9049"], - ["1126", "9059"], - ["1127", "9067"], - ["1128", "9091"], - ["1129", "9103"], - ["1130", "9109"], - ["1131", "9127"], - ["1132", "9133"], - ["1133", "9137"], - ["1134", "9151"], - ["1135", "9157"], - ["1136", "9161"], - ["1137", "9173"], - ["1138", "9181"], - ["1139", "9187"], - ["1140", "9199"], - ["1141", "9203"], - ["1142", "9209"], - ["1143", "9221"], - ["1144", "9227"], - ["1145", "9239"], - ["1146", "9241"], - ["1147", "9257"], - ["1148", "9277"], - ["1149", "9281"], - ["1150", "9283"], - ["1151", "9293"], - ["1152", "9311"], - ["1153", "9319"], - ["1154", "9323"], - ["1155", "9337"], - ["1156", "9341"], - ["1157", "9343"], - ["1158", "9349"], - ["1159", "9371"], - ["1160", "9377"], - ["1161", "9391"], - ["1162", "9397"], - ["1163", "9403"], - ["1164", "9413"], - ["1165", "9419"], - ["1166", "9421"], - ["1167", "9431"], - ["1168", "9433"], - ["1169", "9437"], - ["1170", "9439"], - ["1171", "9461"], - ["1172", "9463"], - ["1173", "9467"], - ["1174", "9473"], - ["1175", "9479"], - ["1176", "9491"], - ["1177", "9497"], - ["1178", "9511"], - ["1179", "9521"], - ["1180", "9533"], - ["1181", "9539"], - ["1182", "9547"], - ["1183", "9551"], - ["1184", "9587"], - ["1185", "9601"], - ["1186", "9613"], - ["1187", "9619"], - ["1188", "9623"], - ["1189", "9629"], - ["1190", "9631"], - ["1191", "9643"], - ["1192", "9649"], - ["1193", "9661"], - ["1194", "9677"], - ["1195", "9679"], - ["1196", "9689"], - ["1197", "9697"], - ["1198", "9719"], - ["1199", "9721"], - ["1200", "9733"], - ["1201", "9739"], - ["1202", "9743"], - ["1203", "9749"], - ["1204", "9767"], - ["1205", "9769"], - ["1206", "9781"], - ["1207", "9787"], - ["1208", "9791"], - ["1209", "9803"], - ["1210", "9811"], - ["1211", "9817"], - ["1212", "9829"], - ["1213", "9833"], - ["1214", "9839"], - ["1215", "9851"], - ["1216", "9857"], - ["1217", "9859"], - ["1218", "9871"], - ["1219", "9883"], - ["1220", "9887"], - ["1221", "9901"], - ["1222", "9907"], - ["1223", "9923"], - ["1224", "9929"], - ["1225", "9931"], - ["1226", "9941"], - ["1227", "9949"], - ["1228", "9967"], - ["1229", "9973"], - ["1230", "10007"], - ["1231", "10009"], - ["1232", "10037"], - ["1233", "10039"], - ["1234", "10061"], - ["1235", "10067"], - ["1236", "10069"], - ["1237", "10079"], - ["1238", "10091"], - ["1239", "10093"], - ["1240", "10099"], - ["1241", "10103"], - ["1242", "10111"], - ["1243", "10133"], - ["1244", "10139"], - ["1245", "10141"], - ["1246", "10151"], - ["1247", "10159"], - ["1248", "10163"], - ["1249", "10169"], - ["1250", "10177"], - ["1251", "10181"], - ["1252", "10193"], - ["1253", "10211"], - ["1254", "10223"], - ["1255", "10243"], - ["1256", "10247"], - ["1257", "10253"], - ["1258", "10259"], - ["1259", "10267"], - ["1260", "10271"], - ["1261", "10273"], - ["1262", "10289"], - ["1263", "10301"], - ["1264", "10303"], - ["1265", "10313"], - ["1266", "10321"], - ["1267", "10331"], - ["1268", "10333"], - ["1269", "10337"], - ["1270", "10343"], - ["1271", "10357"], - ["1272", "10369"], - ["1273", "10391"], - ["1274", "10399"], - ["1275", "10427"], - ["1276", "10429"], - ["1277", "10433"], - ["1278", "10453"], - ["1279", "10457"], - ["1280", "10459"], - ["1281", "10463"], - ["1282", "10477"], - ["1283", "10487"], - ["1284", "10499"], - ["1285", "10501"], - ["1286", "10513"], - ["1287", "10529"], - ["1288", "10531"], - ["1289", "10559"], - ["1290", "10567"], - ["1291", "10589"], - ["1292", "10597"], - ["1293", "10601"], - ["1294", "10607"], - ["1295", "10613"], - ["1296", "10627"], - ["1297", "10631"], - ["1298", "10639"], - ["1299", "10651"], - ["1300", "10657"], - ["1301", "10663"], - ["1302", "10667"], - ["1303", "10687"], - ["1304", "10691"], - ["1305", "10709"], - ["1306", "10711"], - ["1307", "10723"], - ["1308", "10729"], - ["1309", "10733"], - ["1310", "10739"], - ["1311", "10753"], - ["1312", "10771"], - ["1313", "10781"], - ["1314", "10789"], - ["1315", "10799"], - ["1316", "10831"], - ["1317", "10837"], - ["1318", "10847"], - ["1319", "10853"], - ["1320", "10859"], - ["1321", "10861"], - ["1322", "10867"], - ["1323", "10883"], - ["1324", "10889"], - ["1325", "10891"], - ["1326", "10903"], - ["1327", "10909"], - ["1328", "10937"], - ["1329", "10939"], - ["1330", "10949"], - ["1331", "10957"], - ["1332", "10973"], - ["1333", "10979"], - ["1334", "10987"], - ["1335", "10993"], - ["1336", "11003"], - ["1337", "11027"], - ["1338", "11047"], - ["1339", "11057"], - ["1340", "11059"], - ["1341", "11069"], - ["1342", "11071"], - ["1343", "11083"], - ["1344", "11087"], - ["1345", "11093"], - ["1346", "11113"], - ["1347", "11117"], - ["1348", "11119"], - ["1349", "11131"], - ["1350", "11149"], - ["1351", "11159"], - ["1352", "11161"], - ["1353", "11171"], - ["1354", "11173"], - ["1355", "11177"], - ["1356", "11197"], - ["1357", "11213"], - ["1358", "11239"], - ["1359", "11243"], - ["1360", "11251"], - ["1361", "11257"], - ["1362", "11261"], - ["1363", "11273"], - ["1364", "11279"], - ["1365", "11287"], - ["1366", "11299"], - ["1367", "11311"], - ["1368", "11317"], - ["1369", "11321"], - ["1370", "11329"], - ["1371", "11351"], - ["1372", "11353"], - ["1373", "11369"], - ["1374", "11383"], - ["1375", "11393"], - ["1376", "11399"], - ["1377", "11411"], - ["1378", "11423"], - ["1379", "11437"], - ["1380", "11443"], - ["1381", "11447"], - ["1382", "11467"], - ["1383", "11471"], - ["1384", "11483"], - ["1385", "11489"], - ["1386", "11491"], - ["1387", "11497"], - ["1388", "11503"], - ["1389", "11519"], - ["1390", "11527"], - ["1391", "11549"], - ["1392", "11551"], - ["1393", "11579"], - ["1394", "11587"], - ["1395", "11593"], - ["1396", "11597"], - ["1397", "11617"], - ["1398", "11621"], - ["1399", "11633"], - ["1400", "11657"], - ["1401", "11677"], - ["1402", "11681"], - ["1403", "11689"], - ["1404", "11699"], - ["1405", "11701"], - ["1406", "11717"], - ["1407", "11719"], - ["1408", "11731"], - ["1409", "11743"], - ["1410", "11777"], - ["1411", "11779"], - ["1412", "11783"], - ["1413", "11789"], - ["1414", "11801"], - ["1415", "11807"], - ["1416", "11813"], - ["1417", "11821"], - ["1418", "11827"], - ["1419", "11831"], - ["1420", "11833"], - ["1421", "11839"], - ["1422", "11863"], - ["1423", "11867"], - ["1424", "11887"], - ["1425", "11897"], - ["1426", "11903"], - ["1427", "11909"], - ["1428", "11923"], - ["1429", "11927"], - ["1430", "11933"], - ["1431", "11939"], - ["1432", "11941"], - ["1433", "11953"], - ["1434", "11959"], - ["1435", "11969"], - ["1436", "11971"], - ["1437", "11981"], - ["1438", "11987"], - ["1439", "12007"], - ["1440", "12011"], - ["1441", "12037"], - ["1442", "12041"], - ["1443", "12043"], - ["1444", "12049"], - ["1445", "12071"], - ["1446", "12073"], - ["1447", "12097"], - ["1448", "12101"], - ["1449", "12107"], - ["1450", "12109"], - ["1451", "12113"], - ["1452", "12119"], - ["1453", "12143"], - ["1454", "12149"], - ["1455", "12157"], - ["1456", "12161"], - ["1457", "12163"], - ["1458", "12197"], - ["1459", "12203"], - ["1460", "12211"], - ["1461", "12227"], - ["1462", "12239"], - ["1463", "12241"], - ["1464", "12251"], - ["1465", "12253"], - ["1466", "12263"], - ["1467", "12269"], - ["1468", "12277"], - ["1469", "12281"], - ["1470", "12289"], - ["1471", "12301"], - ["1472", "12323"], - ["1473", "12329"], - ["1474", "12343"], - ["1475", "12347"], - ["1476", "12373"], - ["1477", "12377"], - ["1478", "12379"], - ["1479", "12391"], - ["1480", "12401"], - ["1481", "12409"], - ["1482", "12413"], - ["1483", "12421"], - ["1484", "12433"], - ["1485", "12437"], - ["1486", "12451"], - ["1487", "12457"], - ["1488", "12473"], - ["1489", "12479"], - ["1490", "12487"], - ["1491", "12491"], - ["1492", "12497"], - ["1493", "12503"], - ["1494", "12511"], - ["1495", "12517"], - ["1496", "12527"], - ["1497", "12539"], - ["1498", "12541"], - ["1499", "12547"], - ["1500", "12553"], - ["1501", "12569"], - ["1502", "12577"], - ["1503", "12583"], - ["1504", "12589"], - ["1505", "12601"], - ["1506", "12611"], - ["1507", "12613"], - ["1508", "12619"], - ["1509", "12637"], - ["1510", "12641"], - ["1511", "12647"], - ["1512", "12653"], - ["1513", "12659"], - ["1514", "12671"], - ["1515", "12689"], - ["1516", "12697"], - ["1517", "12703"], - ["1518", "12713"], - ["1519", "12721"], - ["1520", "12739"], - ["1521", "12743"], - ["1522", "12757"], - ["1523", "12763"], - ["1524", "12781"], - ["1525", "12791"], - ["1526", "12799"], - ["1527", "12809"], - ["1528", "12821"], - ["1529", "12823"], - ["1530", "12829"], - ["1531", "12841"], - ["1532", "12853"], - ["1533", "12889"], - ["1534", "12893"], - ["1535", "12899"], - ["1536", "12907"], - ["1537", "12911"], - ["1538", "12917"], - ["1539", "12919"], - ["1540", "12923"], - ["1541", "12941"], - ["1542", "12953"], - ["1543", "12959"], - ["1544", "12967"], - ["1545", "12973"], - ["1546", "12979"], - ["1547", "12983"], - ["1548", "13001"], - ["1549", "13003"], - ["1550", "13007"], - ["1551", "13009"], - ["1552", "13033"], - ["1553", "13037"], - ["1554", "13043"], - ["1555", "13049"], - ["1556", "13063"], - ["1557", "13093"], - ["1558", "13099"], - ["1559", "13103"], - ["1560", "13109"], - ["1561", "13121"], - ["1562", "13127"], - ["1563", "13147"], - ["1564", "13151"], - ["1565", "13159"], - ["1566", "13163"], - ["1567", "13171"], - ["1568", "13177"], - ["1569", "13183"], - ["1570", "13187"], - ["1571", "13217"], - ["1572", "13219"], - ["1573", "13229"], - ["1574", "13241"], - ["1575", "13249"], - ["1576", "13259"], - ["1577", "13267"], - ["1578", "13291"], - ["1579", "13297"], - ["1580", "13309"], - ["1581", "13313"], - ["1582", "13327"], - ["1583", "13331"], - ["1584", "13337"], - ["1585", "13339"], - ["1586", "13367"], - ["1587", "13381"], - ["1588", "13397"], - ["1589", "13399"], - ["1590", "13411"], - ["1591", "13417"], - ["1592", "13421"], - ["1593", "13441"], - ["1594", "13451"], - ["1595", "13457"], - ["1596", "13463"], - ["1597", "13469"], - ["1598", "13477"], - ["1599", "13487"], - ["1600", "13499"], - ["1601", "13513"], - ["1602", "13523"], - ["1603", "13537"], - ["1604", "13553"], - ["1605", "13567"], - ["1606", "13577"], - ["1607", "13591"], - ["1608", "13597"], - ["1609", "13613"], - ["1610", "13619"], - ["1611", "13627"], - ["1612", "13633"], - ["1613", "13649"], - ["1614", "13669"], - ["1615", "13679"], - ["1616", "13681"], - ["1617", "13687"], - ["1618", "13691"], - ["1619", "13693"], - ["1620", "13697"], - ["1621", "13709"], - ["1622", "13711"], - ["1623", "13721"], - ["1624", "13723"], - ["1625", "13729"], - ["1626", "13751"], - ["1627", "13757"], - ["1628", "13759"], - ["1629", "13763"], - ["1630", "13781"], - ["1631", "13789"], - ["1632", "13799"], - ["1633", "13807"], - ["1634", "13829"], - ["1635", "13831"], - ["1636", "13841"], - ["1637", "13859"], - ["1638", "13873"], - ["1639", "13877"], - ["1640", "13879"], - ["1641", "13883"], - ["1642", "13901"], - ["1643", "13903"], - ["1644", "13907"], - ["1645", "13913"], - ["1646", "13921"], - ["1647", "13931"], - ["1648", "13933"], - ["1649", "13963"], - ["1650", "13967"], - ["1651", "13997"], - ["1652", "13999"], - ["1653", "14009"], - ["1654", "14011"], - ["1655", "14029"], - ["1656", "14033"], - ["1657", "14051"], - ["1658", "14057"], - ["1659", "14071"], - ["1660", "14081"], - ["1661", "14083"], - ["1662", "14087"], - ["1663", "14107"], - ["1664", "14143"], - ["1665", "14149"], - ["1666", "14153"], - ["1667", "14159"], - ["1668", "14173"], - ["1669", "14177"], - ["1670", "14197"], - ["1671", "14207"], - ["1672", "14221"], - ["1673", "14243"], - ["1674", "14249"], - ["1675", "14251"], - ["1676", "14281"], - ["1677", "14293"], - ["1678", "14303"], - ["1679", "14321"], - ["1680", "14323"], - ["1681", "14327"], - ["1682", "14341"], - ["1683", "14347"], - ["1684", "14369"], - ["1685", "14387"], - ["1686", "14389"], - ["1687", "14401"], - ["1688", "14407"], - ["1689", "14411"], - ["1690", "14419"], - ["1691", "14423"], - ["1692", "14431"], - ["1693", "14437"], - ["1694", "14447"], - ["1695", "14449"], - ["1696", "14461"], - ["1697", "14479"], - ["1698", "14489"], - ["1699", "14503"], - ["1700", "14519"], - ["1701", "14533"], - ["1702", "14537"], - ["1703", "14543"], - ["1704", "14549"], - ["1705", "14551"], - ["1706", "14557"], - ["1707", "14561"], - ["1708", "14563"], - ["1709", "14591"], - ["1710", "14593"], - ["1711", "14621"], - ["1712", "14627"], - ["1713", "14629"], - ["1714", "14633"], - ["1715", "14639"], - ["1716", "14653"], - ["1717", "14657"], - ["1718", "14669"], - ["1719", "14683"], - ["1720", "14699"], - ["1721", "14713"], - ["1722", "14717"], - ["1723", "14723"], - ["1724", "14731"], - ["1725", "14737"], - ["1726", "14741"], - ["1727", "14747"], - ["1728", "14753"], - ["1729", "14759"], - ["1730", "14767"], - ["1731", "14771"], - ["1732", "14779"], - ["1733", "14783"], - ["1734", "14797"], - ["1735", "14813"], - ["1736", "14821"], - ["1737", "14827"], - ["1738", "14831"], - ["1739", "14843"], - ["1740", "14851"], - ["1741", "14867"], - ["1742", "14869"], - ["1743", "14879"], - ["1744", "14887"], - ["1745", "14891"], - ["1746", "14897"], - ["1747", "14923"], - ["1748", "14929"], - ["1749", "14939"], - ["1750", "14947"], - ["1751", "14951"], - ["1752", "14957"], - ["1753", "14969"], - ["1754", "14983"], - ["1755", "15013"], - ["1756", "15017"], - ["1757", "15031"], - ["1758", "15053"], - ["1759", "15061"], - ["1760", "15073"], - ["1761", "15077"], - ["1762", "15083"], - ["1763", "15091"], - ["1764", "15101"], - ["1765", "15107"], - ["1766", "15121"], - ["1767", "15131"], - ["1768", "15137"], - ["1769", "15139"], - ["1770", "15149"], - ["1771", "15161"], - ["1772", "15173"], - ["1773", "15187"], - ["1774", "15193"], - ["1775", "15199"], - ["1776", "15217"], - ["1777", "15227"], - ["1778", "15233"], - ["1779", "15241"], - ["1780", "15259"], - ["1781", "15263"], - ["1782", "15269"], - ["1783", "15271"], - ["1784", "15277"], - ["1785", "15287"], - ["1786", "15289"], - ["1787", "15299"], - ["1788", "15307"], - ["1789", "15313"], - ["1790", "15319"], - ["1791", "15329"], - ["1792", "15331"], - ["1793", "15349"], - ["1794", "15359"], - ["1795", "15361"], - ["1796", "15373"], - ["1797", "15377"], - ["1798", "15383"], - ["1799", "15391"], - ["1800", "15401"], - ["1801", "15413"], - ["1802", "15427"], - ["1803", "15439"], - ["1804", "15443"], - ["1805", "15451"], - ["1806", "15461"], - ["1807", "15467"], - ["1808", "15473"], - ["1809", "15493"], - ["1810", "15497"], - ["1811", "15511"], - ["1812", "15527"], - ["1813", "15541"], - ["1814", "15551"], - ["1815", "15559"], - ["1816", "15569"], - ["1817", "15581"], - ["1818", "15583"], - ["1819", "15601"], - ["1820", "15607"], - ["1821", "15619"], - ["1822", "15629"], - ["1823", "15641"], - ["1824", "15643"], - ["1825", "15647"], - ["1826", "15649"], - ["1827", "15661"], - ["1828", "15667"], - ["1829", "15671"], - ["1830", "15679"], - ["1831", "15683"], - ["1832", "15727"], - ["1833", "15731"], - ["1834", "15733"], - ["1835", "15737"], - ["1836", "15739"], - ["1837", "15749"], - ["1838", "15761"], - ["1839", "15767"], - ["1840", "15773"], - ["1841", "15787"], - ["1842", "15791"], - ["1843", "15797"], - ["1844", "15803"], - ["1845", "15809"], - ["1846", "15817"], - ["1847", "15823"], - ["1848", "15859"], - ["1849", "15877"], - ["1850", "15881"], - ["1851", "15887"], - ["1852", "15889"], - ["1853", "15901"], - ["1854", "15907"], - ["1855", "15913"], - ["1856", "15919"], - ["1857", "15923"], - ["1858", "15937"], - ["1859", "15959"], - ["1860", "15971"], - ["1861", "15973"], - ["1862", "15991"], - ["1863", "16001"], - ["1864", "16007"], - ["1865", "16033"], - ["1866", "16057"], - ["1867", "16061"], - ["1868", "16063"], - ["1869", "16067"], - ["1870", "16069"], - ["1871", "16073"], - ["1872", "16087"], - ["1873", "16091"], - ["1874", "16097"], - ["1875", "16103"], - ["1876", "16111"], - ["1877", "16127"], - ["1878", "16139"], - ["1879", "16141"], - ["1880", "16183"], - ["1881", "16187"], - ["1882", "16189"], - ["1883", "16193"], - ["1884", "16217"], - ["1885", "16223"], - ["1886", "16229"], - ["1887", "16231"], - ["1888", "16249"], - ["1889", "16253"], - ["1890", "16267"], - ["1891", "16273"], - ["1892", "16301"], - ["1893", "16319"], - ["1894", "16333"], - ["1895", "16339"], - ["1896", "16349"], - ["1897", "16361"], - ["1898", "16363"], - ["1899", "16369"], - ["1900", "16381"], - ["1901", "16411"], - ["1902", "16417"], - ["1903", "16421"], - ["1904", "16427"], - ["1905", "16433"], - ["1906", "16447"], - ["1907", "16451"], - ["1908", "16453"], - ["1909", "16477"], - ["1910", "16481"], - ["1911", "16487"], - ["1912", "16493"], - ["1913", "16519"], - ["1914", "16529"], - ["1915", "16547"], - ["1916", "16553"], - ["1917", "16561"], - ["1918", "16567"], - ["1919", "16573"], - ["1920", "16603"], - ["1921", "16607"], - ["1922", "16619"], - ["1923", "16631"], - ["1924", "16633"], - ["1925", "16649"], - ["1926", "16651"], - ["1927", "16657"], - ["1928", "16661"], - ["1929", "16673"], - ["1930", "16691"], - ["1931", "16693"], - ["1932", "16699"], - ["1933", "16703"], - ["1934", "16729"], - ["1935", "16741"], - ["1936", "16747"], - ["1937", "16759"], - ["1938", "16763"], - ["1939", "16787"], - ["1940", "16811"], - ["1941", "16823"], - ["1942", "16829"], - ["1943", "16831"], - ["1944", "16843"], - ["1945", "16871"], - ["1946", "16879"]]}}]} - ============================================================================== - -================================================================================ -MERGE RESULT: ✅ SUCCESS -================================================================================ -Final result length: 54033 chars -Final result (COMPLETE): -================================================================================ -{ - "elements": [ - { - "type": "table", - "content": { - "headers": ["Position", "Primzahl"], - "rows": [ - ["1", "2"], - ["2", "3"], - ["3", "5"], - ["4", "7"], - ["5", "11"], - ["6", "13"], - ["7", "17"], - ["8", "19"], - ["9", "23"], - ["10", "29"], - ["11", "31"], - ["12", "37"], - ["13", "41"], - ["14", "43"], - ["15", "47"], - ["16", "53"], - ["17", "59"], - ["18", "61"], - ["19", "67"], - ["20", "71"], - ["21", "73"], - ["22", "79"], - ["23", "83"], - ["24", "89"], - ["25", "97"], - ["26", "101"], - ["27", "103"], - ["28", "107"], - ["29", "109"], - ["30", "113"], - ["31", "127"], - ["32", "131"], - ["33", "137"], - ["34", "139"], - ["35", "149"], - ["36", "151"], - ["37", "157"], - ["38", "163"], - ["39", "167"], - ["40", "173"], - ["41", "179"], - ["42", "181"], - ["43", "191"], - ["44", "193"], - ["45", "197"], - ["46", "199"], - ["47", "211"], - ["48", "223"], - ["49", "227"], - ["50", "229"], - ["51", "233"], - ["52", "239"], - ["53", "241"], - ["54", "251"], - ["55", "257"], - ["56", "263"], - ["57", "269"], - ["58", "271"], - ["59", "277"], - ["60", "281"], - ["61", "283"], - ["62", "293"], - ["63", "307"], - ["64", "311"], - ["65", "313"], - ["66", "317"], - ["67", "331"], - ["68", "337"], - ["69", "347"], - ["70", "349"], - ["71", "353"], - ["72", "359"], - ["73", "367"], - ["74", "373"], - ["75", "379"], - ["76", "383"], - ["77", "389"], - ["78", "397"], - ["79", "401"], - ["80", "409"], - ["81", "419"], - ["82", "421"], - ["83", "431"], - ["84", "433"], - ["85", "439"], - ["86", "443"], - ["87", "449"], - ["88", "457"], - ["89", "461"], - ["90", "463"], - ["91", "467"], - ["92", "479"], - ["93", "487"], - ["94", "491"], - ["95", "499"], - ["96", "503"], - ["97", "509"], - ["98", "521"], - ["99", "523"], - ["100", "541"], - ["101", "547"], - ["102", "557"], - ["103", "563"], - ["104", "569"], - ["105", "571"], - ["106", "577"], - ["107", "587"], - ["108", "593"], - ["109", "599"], - ["110", "601"], - ["111", "607"], - ["112", "613"], - ["113", "617"], - ["114", "619"], - ["115", "631"], - ["116", "641"], - ["117", "643"], - ["118", "647"], - ["119", "653"], - ["120", "659"], - ["121", "661"], - ["122", "673"], - ["123", "677"], - ["124", "683"], - ["125", "691"], - ["126", "701"], - ["127", "709"], - ["128", "719"], - ["129", "727"], - ["130", "733"], - ["131", "739"], - ["132", "743"], - ["133", "751"], - ["134", "757"], - ["135", "761"], - ["136", "769"], - ["137", "773"], - ["138", "787"], - ["139", "797"], - ["140", "809"], - ["141", "811"], - ["142", "821"], - ["143", "823"], - ["144", "827"], - ["145", "829"], - ["146", "839"], - ["147", "853"], - ["148", "857"], - ["149", "859"], - ["150", "863"], - ["151", "877"], - ["152", "881"], - ["153", "883"], - ["154", "887"], - ["155", "907"], - ["156", "911"], - ["157", "919"], - ["158", "929"], - ["159", "937"], - ["160", "941"], - ["161", "947"], - ["162", "953"], - ["163", "967"], - ["164", "971"], - ["165", "977"], - ["166", "983"], - ["167", "991"], - ["168", "997"], - ["169", "1009"], - ["170", "1013"], - ["171", "1019"], - ["172", "1021"], - ["173", "1031"], - ["174", "1033"], - ["175", "1039"], - ["176", "1049"], - ["177", "1051"], - ["178", "1061"], - ["179", "1063"], - ["180", "1069"], - ["181", "1087"], - ["182", "1091"], - ["183", "1093"], - ["184", "1097"], - ["185", "1103"], - ["186", "1109"], - ["187", "1117"], - ["188", "1123"], - ["189", "1129"], - ["190", "1151"], - ["191", "1153"], - ["192", "1163"], - ["193", "1171"], - ["194", "1181"], - ["195", "1187"], - ["196", "1193"], - ["197", "1201"], - ["198", "1213"], - ["199", "1217"], - ["200", "1223"], - ["201", "1229"], - ["202", "1231"], - ["203", "1237"], - ["204", "1249"], - ["205", "1259"], - ["206", "1277"], - ["207", "1279"], - ["208", "1283"], - ["209", "1289"], - ["210", "1291"], - ["211", "1297"], - ["212", "1301"], - ["213", "1303"], - ["214", "1307"], - ["215", "1319"], - ["216", "1321"], - ["217", "1327"], - ["218", "1361"], - ["219", "1367"], - ["220", "1373"], - ["221", "1381"], - ["222", "1399"], - ["223", "1409"], - ["224", "1423"], - ["225", "1427"], - ["226", "1429"], - ["227", "1433"], - ["228", "1439"], - ["229", "1447"], - ["230", "1451"], - ["231", "1453"], - ["232", "1459"], - ["233", "1471"], - ["234", "1481"], - ["235", "1483"], - ["236", "1487"], - ["237", "1489"], - ["238", "1493"], - ["239", "1499"], - ["240", "1511"], - ["241", "1523"], - ["242", "1531"], - ["243", "1543"], - ["244", "1549"], - ["245", "1553"], - ["246", "1559"], - ["247", "1567"], - ["248", "1571"], - ["249", "1579"], - ["250", "1583"], - ["251", "1597"], - ["252", "1601"], - ["253", "1607"], - ["254", "1609"], - ["255", "1613"], - ["256", "1619"], - ["257", "1621"], - ["258", "1627"], - ["259", "1637"], - ["260", "1657"], - ["261", "1663"], - ["262", "1667"], - ["263", "1669"], - ["264", "1693"], - ["265", "1697"], - ["266", "1699"], - ["267", "1709"], - ["268", "1721"], - ["269", "1723"], - ["270", "1733"], - ["271", "1741"], - ["272", "1747"], - ["273", "1753"], - ["274", "1759"], - ["275", "1777"], - ["276", "1783"], - ["277", "1787"], - ["278", "1789"], - ["279", "1801"], - ["280", "1811"], - ["281", "1823"], - ["282", "1831"], - ["283", "1847"], - ["284", "1861"], - ["285", "1867"], - ["286", "1871"], - ["287", "1873"], - ["288", "1877"], - ["289", "1879"], - ["290", "1889"], - ["291", "1901"], - ["292", "1907"], - ["293", "1913"], - ["294", "1931"], - ["295", "1933"], - ["296", "1949"], - ["297", "1951"], - ["298", "1973"], - ["299", "1979"], - ["300", "1987"], - ["301", "1993"], - ["302", "1997"], - ["303", "1999"], - ["304", "2003"], - ["305", "2011"], - ["306", "2017"], - ["307", "2027"], - ["308", "2029"], - ["309", "2039"], - ["310", "2053"], - ["311", "2063"], - ["312", "2069"], - ["313", "2081"], - ["314", "2083"], - ["315", "2087"], - ["316", "2089"], - ["317", "2099"], - ["318", "2111"], - ["319", "2113"], - ["320", "2129"], - ["321", "2131"], - ["322", "2137"], - ["323", "2141"], - ["324", "2143"], - ["325", "2153"], - ["326", "2161"], - ["327", "2179"], - ["328", "2203"], - ["329", "2207"], - ["330", "2213"], - ["331", "2221"], - ["332", "2237"], - ["333", "2239"], - ["334", "2243"], - ["335", "2251"], - ["336", "2267"], - ["337", "2269"], - ["338", "2273"], - ["339", "2281"], - ["340", "2287"], - ["341", "2293"], - ["342", "2297"], - ["343", "2309"], - ["344", "2311"], - ["345", "2333"], - ["346", "2339"], - ["347", "2341"], - ["348", "2347"], - ["349", "2351"], - ["350", "2357"], - ["351", "2371"], - ["352", "2377"], - ["353", "2381"], - ["354", "2383"], - ["355", "2389"], - ["356", "2393"], - ["357", "2399"], - ["358", "2411"], - ["359", "2417"], - ["360", "2423"], - ["361", "2437"], - ["362", "2441"], - ["363", "2447"], - ["364", "2459"], - ["365", "2467"], - ["366", "2473"], - ["367", "2477"], - ["368", "2503"], - ["369", "2521"], - ["370", "2531"], - ["371", "2539"], - ["372", "2543"], - ["373", "2549"], - ["374", "2551"], - ["375", "2557"], - ["376", "2579"], - ["377", "2591"], - ["378", "2593"], - ["379", "2609"], - ["380", "2617"], - ["381", "2621"], - ["382", "2633"], - ["383", "2647"], - ["384", "2657"], - ["385", "2659"], - ["386", "2663"], - ["387", "2671"], - ["388", "2677"], - ["389", "2683"], - ["390", "2687"], - ["391", "2689"], - ["392", "2693"], - ["393", "2699"], - ["394", "2707"], - ["395", "2711"], - ["396", "2713"], - ["397", "2719"], - ["398", "2729"], - ["399", "2731"], - ["400", "2741"], - ["401", "2749"], - ["402", "2753"], - ["403", "2767"], - ["404", "2777"], - ["405", "2789"], - ["406", "2791"], - ["407", "2797"], - ["408", "2801"], - ["409", "2803"], - ["410", "2819"], - ["411", "2833"], - ["412", "2837"], - ["413", "2843"], - ["414", "2851"], - ["415", "2857"], - ["416", "2861"], - ["417", "2879"], - ["418", "2887"], - ["419", "2897"], - ["420", "2903"], - ["421", "2909"], - ["422", "2917"], - ["423", "2927"], - ["424", "2939"], - ["425", "2953"], - ["426", "2957"], - ["427", "2963"], - ["428", "2969"], - ["429", "2971"], - ["430", "2999"], - ["431", "3001"], - ["432", "3011"], - ["433", "3019"], - ["434", "3023"], - ["435", "3037"], - ["436", "3041"], - ["437", "3049"], - ["438", "3061"], - ["439", "3067"], - ["440", "3079"], - ["441", "3083"], - ["442", "3089"], - ["443", "3109"], - ["444", "3119"], - ["445", "3121"], - ["446", "3137"], - ["447", "3163"], - ["448", "3167"], - ["449", "3169"], - ["450", "3181"], - ["451", "3187"], - ["452", "3191"], - ["453", "3203"], - ["454", "3209"], - ["455", "3217"], - ["456", "3221"], - ["457", "3229"], - ["458", "3251"], - ["459", "3253"], - ["460", "3257"], - ["461", "3259"], - ["462", "3271"], - ["463", "3299"], - ["464", "3301"], - ["465", "3307"], - ["466", "3313"], - ["467", "3319"], - ["468", "3323"], - ["469", "3329"], - ["470", "3331"], - ["471", "3343"], - ["472", "3347"], - ["473", "3359"], - ["474", "3361"], - ["475", "3371"], - ["476", "3373"], - ["477", "3389"], - ["478", "3391"], - ["479", "3407"], - ["480", "3413"], - ["481", "3433"], - ["482", "3449"], - ["483", "3457"], - ["484", "3461"], - ["485", "3463"], - ["486", "3467"], - ["487", "3469"], - ["488", "3491"], - ["489", "3499"], - ["490", "3511"], - ["491", "3517"], - ["492", "3527"], - ["493", "3529"], - ["494", "3533"], - ["495", "3539"], - ["496", "3541"], - ["497", "3547"], - ["498", "3557"], - ["499", "3559"], - ["500", "3571"], - ["501", "3581"], - ["502", "3583"], - ["503", "3593"], - ["504", "3607"], - ["505", "3613"], - ["506", "3617"], - ["507", "3623"], - ["508", "3631"], - ["509", "3637"], - ["510", "3643"], - ["511", "3659"], - ["512", "3671"], - ["513", "3673"], - ["514", "3677"], - ["515", "3691"], - ["516", "3697"], - ["517", "3701"], - ["518", "3709"], - ["519", "3719"], - ["520", "3727"], - ["521", "3733"], - ["522", "3739"], - ["523", "3761"], - ["524", "3767"], - ["525", "3769"], - ["526", "3779"], - ["527", "3793"], - ["528", "3797"], - ["529", "3803"], - ["530", "3821"], - ["531", "3823"], - ["532", "3833"], - ["533", "3847"], - ["534", "3851"], - ["535", "3853"], - ["536", "3863"], - ["537", "3877"], - ["538", "3881"], - ["539", "3889"], - ["540", "3907"], - ["541", "3911"], - ["542", "3917"], - ["543", "3919"], - ["544", "3923"], - ["545", "3929"], - ["546", "3931"], - ["547", "3943"], - ["548", "3947"], - ["549", "3967"], - ["550", "3989"], - ["551", "4001"], - ["552", "4003"], - ["553", "4007"], - ["554", "4013"], - ["555", "4019"], - ["556", "4021"], - ["557", "4027"], - ["558", "4049"], - ["559", "4051"], - ["560", "4057"], - ["561", "4073"], - ["562", "4079"], - ["563", "4091"], - ["564", "4093"], - ["565", "4099"], - ["566", "4111"], - ["567", "4127"], - ["568", "4129"], - ["569", "4133"], - ["570", "4139"], - ["571", "4153"], - ["572", "4157"], - ["573", "4159"], - ["574", "4177"], - ["575", "4201"], - ["576", "4211"], - ["577", "4217"], - ["578", "4219"], - ["579", "4229"], - ["580", "4231"], - ["581", "4241"], - ["582", "4243"], - ["583", "4253"], - ["584", "4259"], - ["585", "4261"], - ["586", "4271"], - ["587", "4273"], - ["588", "4283"], - ["589", "4289"], - ["590", "4297"], - ["591", "4327"], - ["592", "4337"], - ["593", "4339"], - ["594", "4349"], - ["595", "4357"], - ["596", "4363"], - ["597", "4373"], - ["598", "4391"], - ["599", "4397"], - ["600", "4409"], - ["601", "4421"], - ["602", "4423"], - ["603", "4441"], - ["604", "4447"], - ["605", "4451"], - ["606", "4457"], - ["607", "4463"], - ["608", "4481"], - ["609", "4483"], - ["610", "4493"], - ["611", "4507"], - ["612", "4513"], - ["613", "4517"], - ["614", "4519"], - ["615", "4523"], - ["616", "4547"], - ["617", "4549"], - ["618", "4561"], - ["619", "4567"], - ["620", "4583"], - ["621", "4591"], - ["622", "4597"], - ["623", "4603"], - ["624", "4621"], - ["625", "4637"], - ["626", "4639"], - ["627", "4643"], - ["628", "4649"], - ["629", "4651"], - ["630", "4657"], - ["631", "4663"], - ["632", "4673"], - ["633", "4679"], - ["634", "4691"], - ["635", "4703"], - ["636", "4721"], - ["637", "4723"], - ["638", "4729"], - ["639", "4733"], - ["640", "4751"], - ["641", "4759"], - ["642", "4783"], - ["643", "4787"], - ["644", "4789"], - ["645", "4793"], - ["646", "4799"], - ["647", "4801"], - ["648", "4813"], - ["649", "4817"], - ["650", "4831"], - ["651", "4861"], - ["652", "4871"], - ["653", "4877"], - ["654", "4889"], - ["655", "4903"], - ["656", "4909"], - ["657", "4919"], - ["658", "4931"], - ["659", "4933"], - ["660", "4937"], - ["661", "4943"], - ["662", "4951"], - ["663", "4957"], - ["664", "4967"], - ["665", "4969"], - ["666", "4973"], - ["667", "4987"], - ["668", "4993"], - ["669", "4999"], - ["670", "5003"], - ["671", "5009"], - ["672", "5011"], - ["673", "5021"], - ["674", "5023"], - ["675", "5039"], - ["676", "5051"], - ["677", "5059"], - ["678", "5077"], - ["679", "5081"], - ["680", "5087"], - ["681", "5099"], - ["682", "5101"], - ["683", "5107"], - ["684", "5113"], - ["685", "5119"], - ["686", "5147"], - ["687", "5153"], - ["688", "5167"], - ["689", "5171"], - ["690", "5179"], - ["691", "5189"], - ["692", "5197"], - ["693", "5209"], - ["694", "5227"], - ["695", "5231"], - ["696", "5233"], - ["697", "5237"], - ["698", "5261"], - ["699", "5273"], - ["700", "5279"], - ["701", "5281"], - ["702", "5297"], - ["703", "5303"], - ["704", "5309"], - ["705", "5323"], - ["706", "5333"], - ["707", "5347"], - ["708", "5351"], - ["709", "5381"], - ["710", "5387"], - ["711", "5393"], - ["712", "5399"], - ["713", "5407"], - ["714", "5413"], - ["715", "5417"], - ["716", "5419"], - ["717", "5431"], - ["718", "5437"], - ["719", "5441"], - ["720", "5443"], - ["721", "5449"], - ["722", "5471"], - ["723", "5477"], - ["724", "5479"], - ["725", "5483"], - ["726", "5501"], - ["727", "5503"], - ["728", "5507"], - ["729", "5519"], - ["730", "5521"], - ["731", "5527"], - ["732", "5531"], - ["733", "5557"], - ["734", "5563"], - ["735", "5569"], - ["736", "5573"], - ["737", "5581"], - ["738", "5591"], - ["739", "5623"], - ["740", "5639"], - ["741", "5641"], - ["742", "5647"], - ["743", "5651"], - ["744", "5653"], - ["745", "5657"], - ["746", "5659"], - ["747", "5669"], - ["748", "5683"], - ["749", "5689"], - ["750", "5693"], - ["751", "5701"], - ["752", "5711"], - ["753", "5717"], - ["754", "5737"], - ["755", "5741"], - ["756", "5743"], - ["757", "5749"], - ["758", "5779"], - ["759", "5783"], - ["760", "5791"], - ["761", "5801"], - ["762", "5807"], - ["763", "5813"], - ["764", "5821"], - ["765", "5827"], - ["766", "5839"], - ["767", "5843"], - ["768", "5849"], - ["769", "5851"], - ["770", "5857"], - ["771", "5861"], - ["772", "5867"], - ["773", "5869"], - ["774", "5879"], - ["775", "5881"], - ["776", "5897"], - ["777", "5903"], - ["778", "5923"], - ["779", "5927"], - ["780", "5939"], - ["781", "5953"], - ["782", "5981"], - ["783", "5987"], - ["784", "6007"], - ["785", "6011"], - ["786", "6029"], - ["787", "6037"], - ["788", "6043"], - ["789", "6047"], - ["790", "6053"], - ["791", "6067"], - ["792", "6073"], - ["793", "6079"], - ["794", "6089"], - ["795", "6091"], - ["796", "6101"], - ["797", "6113"], - ["798", "6121"], - ["799", "6131"], - ["800", "6133"], - ["801", "6143"], - ["802", "6151"], - ["803", "6163"], - ["804", "6173"], - ["805", "6197"], - ["806", "6199"], - ["807", "6203"], - ["808", "6211"], - ["809", "6217"], - ["810", "6221"], - ["811", "6229"], - ["812", "6247"], - ["813", "6257"], - ["814", "6263"], - ["815", "6269"], - ["816", "6271"], - ["817", "6277"], - ["818", "6287"], - ["819", "6299"], - ["820", "6301"], - ["821", "6311"], - ["822", "6317"], - ["823", "6323"], - ["824", "6329"], - ["825", "6337"], - ["826", "6343"], - ["827", "6353"], - ["828", "6359"], - ["829", "6361"], - ["830", "6367"], - ["831", "6373"], - ["832", "6379"], - ["833", "6389"], - ["834", "6397"], - ["835", "6421"], - ["836", "6427"], - ["837", "6449"], - ["838", "6451"], - ["839", "6469"], - ["840", "6473"], - ["841", "6481"], - ["842", "6491"], - ["843", "6521"], - ["844", "6529"], - ["845", "6547"], - ["846", "6551"], - ["847", "6553"], - ["848", "6563"], - ["849", "6569"], - ["850", "6571"], - ["851", "6577"], - ["852", "6581"], - ["853", "6599"], - ["854", "6607"], - ["855", "6619"], - ["856", "6637"], - ["857", "6653"], - ["858", "6659"], - ["859", "6661"], - ["860", "6673"], - ["861", "6679"], - ["862", "6689"], - ["863", "6691"], - ["864", "6701"], - ["865", "6703"], - ["866", "6709"], - ["867", "6719"], - ["868", "6733"], - ["869", "6737"], - ["870", "6761"], - ["871", "6763"], - ["872", "6779"], - ["873", "6781"], - ["874", "6791"], - ["875", "6793"], - ["876", "6803"], - ["877", "6823"], - ["878", "6827"], - ["879", "6829"], - ["880", "6833"], - ["881", "6841"], - ["882", "6857"], - ["883", "6863"], - ["884", "6869"], - ["885", "6871"], - ["886", "6883"], - ["887", "6899"], - ["888", "6907"], - ["889", "6911"], - ["890", "6917"], - ["891", "6947"], - ["892", "6949"], - ["893", "6959"], - ["894", "6961"], - ["895", "6967"], - ["896", "6971"], - ["897", "6977"], - ["898", "6983"], - ["899", "6991"], - ["900", "6997"], - ["901", "7001"], - ["902", "7013"], - ["903", "7019"], - ["904", "7027"], - ["905", "7039"], - ["906", "7043"], - ["907", "7057"], - ["908", "7069"], - ["909", "7079"], - ["910", "7103"], - ["911", "7109"], - ["912", "7121"], - ["913", "7127"], - ["914", "7129"], - ["915", "7151"], - ["916", "7159"], - ["917", "7177"], - ["918", "7187"], - ["919", "7193"], - ["920", "7207"], - ["921", "7211"], - ["922", "7213"], - ["923", "7219"], - ["924", "7229"], - ["925", "7237"], - ["926", "7243"], - ["927", "7247"], - ["928", "7253"], - ["929", "7283"], - ["930", "7297"], - ["931", "7307"], - ["932", "7309"], - ["933", "7321"], - ["934", "7331"], - ["935", "7333"], - ["936", "7349"], - ["937", "7351"], - ["938", "7369"], - ["939", "7393"], - ["940", "7411"], - ["941", "7417"], - ["942", "7433"], - ["943", "7451"], - ["944", "7457"], - ["945", "7459"], - ["946", "7477"], - ["947", "7481"], - ["948", "7487"], - ["949", "7489"], - ["950", "7499"], - ["951", "7507"], - ["952", "7517"], - ["953", "7523"], - ["954", "7529"], - ["955", "7537"], - ["956", "7541"], - ["957", "7547"], - ["958", "7549"], - ["959", "7559"], - ["960", "7561"], - ["961", "7573"], - ["962", "7577"], - ["963", "7583"], - ["964", "7589"], - ["965", "7591"], - ["966", "7603"], - ["967", "7607"], - ["968", "7621"], - ["969", "7639"], - ["970", "7643"], - ["971", "7649"], - ["972", "7669"], - ["973", "7673"], - ["974", "7681"], - ["975", "7687"], - ["976", "7691"], - ["977", "7699"], - ["978", "7703"], - ["979", "7717"], - ["980", "7723"], - ["981", "7727"], - ["982", "7741"], - ["983", "7753"], - ["984", "7757"], - ["985", "7759"], - ["986", "7789"], - ["987", "7793"], - ["988", "7817"], - ["989", "7823"], - ["990", "7829"], - ["991", "7841"], - ["992", "7853"], - ["993", "7867"], - ["994", "7873"], - ["995", "7877"], - ["996", "7879"], - ["997", "7883"], - ["998", "7901"], - ["999", "7907"], - ["1000", "7919"], - ["1001", "7927"], - ["1002", "7933"], - ["1003", "7937"], - ["1004", "7949"], - ["1005", "7951"], - ["1006", "7963"], - ["1007", "7993"], - ["1008", "8009"], - ["1009", "8011"], - ["1010", "8017"], - ["1011", "8039"], - ["1012", "8053"], - ["1013", "8059"], - ["1014", "8069"], - ["1015", "8081"], - ["1016", "8087"], - ["1017", "8089"], - ["1018", "8093"], - ["1019", "8101"], - ["1020", "8111"], - ["1021", "8117"], - ["1022", "8123"], - ["1023", "8147"], - ["1024", "8161"], - ["1025", "8167"], - ["1026", "8171"], - ["1027", "8179"], - ["1028", "8191"], - ["1029", "8209"], - ["1030", "8219"], - ["1031", "8221"], - ["1032", "8231"], - ["1033", "8233"], - ["1034", "8237"], - ["1035", "8243"], - ["1036", "8263"], - ["1037", "8269"], - ["1038", "8273"], - ["1039", "8287"], - ["1040", "8291"], - ["1041", "8293"], - ["1042", "8297"], - ["1043", "8311"], - ["1044", "8317"], - ["1045", "8329"], - ["1046", "8353"], - ["1047", "8363"], - ["1048", "8369"], - ["1049", "8377"], - ["1050", "8387"], - ["1051", "8389"], - ["1052", "8419"], - ["1053", "8423"], - ["1054", "8429"], - ["1055", "8431"], - ["1056", "8443"], - ["1057", "8447"], - ["1058", "8461"], - ["1059", "8467"], - ["1060", "8501"], - ["1061", "8513"], - ["1062", "8521"], - ["1063", "8527"], - ["1064", "8537"], - ["1065", "8539"], - ["1066", "8543"], - ["1067", "8563"], - ["1068", "8573"], - ["1069", "8581"], - ["1070", "8597"], - ["1071", "8599"], - ["1072", "8609"], - ["1073", "8623"], - ["1074", "8627"], - ["1075", "8629"], - ["1076", "8641"], - ["1077", "8647"], - ["1078", "8663"], - ["1079", "8669"], - ["1080", "8677"], - ["1081", "8681"], - ["1082", "8689"], - ["1083", "8693"], - ["1084", "8699"], - ["1085", "8707"], - ["1086", "8713"], - ["1087", "8719"], - ["1088", "8731"], - ["1089", "8737"], - ["1090", "8741"], - ["1091", "8747"], - ["1092", "8753"], - ["1093", "8761"], - ["1094", "8779"], - ["1095", "8783"], - ["1096", "8803"], - ["1097", "8807"], - ["1098", "8819"], - ["1099", "8821"], - ["1100", "8831"], - ["1101", "8837"], - ["1102", "8839"], - ["1103", "8849"], - ["1104", "8861"], - ["1105", "8863"], - ["1106", "8867"], - ["1107", "8887"], - ["1108", "8893"], - ["1109", "8923"], - ["1110", "8929"], - ["1111", "8933"], - ["1112", "8941"], - ["1113", "8951"], - ["1114", "8963"], - ["1115", "8969"], - ["1116", "8971"], - ["1117", "8999"], - ["1118", "9001"], - ["1119", "9007"], - ["1120", "9011"], - ["1121", "9013"], - ["1122", "9029"], - ["1123", "9041"], - ["1124", "9043"], - ["1125", "9049"], - ["1126", "9059"], - ["1127", "9067"], - ["1128", "9091"], - ["1129", "9103"], - ["1130", "9109"], - ["1131", "9127"], - ["1132", "9133"], - ["1133", "9137"], - ["1134", "9151"], - ["1135", "9157"], - ["1136", "9161"], - ["1137", "9173"], - ["1138", "9181"], - ["1139", "9187"], - ["1140", "9199"], - ["1141", "9203"], - ["1142", "9209"], - ["1143", "9221"], - ["1144", "9227"], - ["1145", "9239"], - ["1146", "9241"], - ["1147", "9257"], - ["1148", "9277"], - ["1149", "9281"], - ["1150", "9283"], - ["1151", "9293"], - ["1152", "9311"], - ["1153", "9319"], - ["1154", "9323"], - ["1155", "9337"], - ["1156", "9341"], - ["1157", "9343"], - ["1158", "9349"], - ["1159", "9371"], - ["1160", "9377"], - ["1161", "9391"], - ["1162", "9397"], - ["1163", "9403"], - ["1164", "9413"], - ["1165", "9419"], - ["1166", "9421"], - ["1167", "9431"], - ["1168", "9433"], - ["1169", "9437"], - ["1170", "9439"], - ["1171", "9461"], - ["1172", "9463"], - ["1173", "9467"], - ["1174", "9473"], - ["1175", "9479"], - ["1176", "9491"], - ["1177", "9497"], - ["1178", "9511"], - ["1179", "9521"], - ["1180", "9533"], - ["1181", "9539"], - ["1182", "9547"], - ["1183", "9551"], - ["1184", "9587"], - ["1185", "9601"], - ["1186", "9613"], - ["1187", "9619"], - ["1188", "9623"], - ["1189", "9629"], - ["1190", "9631"], - ["1191", "9643"], - ["1192", "9649"], - ["1193", "9661"], - ["1194", "9677"], - ["1195", "9679"], - ["1196", "9689"], - ["1197", "9697"], - ["1198", "9719"], - ["1199", "9721"], - ["1200", "9733"], - ["1201", "9739"], - ["1202", "9743"], - ["1203", "9749"], - ["1204", "9767"], - ["1205", "9769"], - ["1206", "9781"], - ["1207", "9787"], - ["1208", "9791"], - ["1209", "9803"], - ["1210", "9811"], - ["1211", "9817"], - ["1212", "9829"], - ["1213", "9833"], - ["1214", "9839"], - ["1215", "9851"], - ["1216", "9857"], - ["1217", "9859"], - ["1218", "9871"], - ["1219", "9883"], - ["1220", "9887"], - ["1221", "9901"], - ["1222", "9907"], - ["1223", "9923"], - ["1224", "9929"], - ["1225", "9931"], - ["1226", "9941"], - ["1227", "9949"], - ["1228", "9967"], - ["1229", "9973"], - ["1230", "10007"], - ["1231", "10009"], - ["1232", "10037"], - ["1233", "10039"], - ["1234", "10061"], - ["1235", "10067"], - ["1236", "10069"], - ["1237", "10079"], - ["1238", "10091"], - ["1239", "10093"], - ["1240", "10099"], - ["1241", "10103"], - ["1242", "10111"], - ["1243", "10133"], - ["1244", "10139"], - ["1245", "10141"], - ["1246", "10151"], - ["1247", "10159"], - ["1248", "10163"], - ["1249", "10169"], - ["1250", "10177"], - ["1251", "10181"], - ["1252", "10193"], - ["1253", "10211"], - ["1254", "10223"], - ["1255", "10243"], - ["1256", "10247"], - ["1257", "10253"], - ["1258", "10259"], - ["1259", "10267"], - ["1260", "10271"], - ["1261", "10273"], - ["1262", "10289"], - ["1263", "10301"], - ["1264", "10303"], - ["1265", "10313"], - ["1266", "10321"], - ["1267", "10331"], - ["1268", "10333"], - ["1269", "10337"], - ["1270", "10343"], - ["1271", "10357"], - ["1272", "10369"], - ["1273", "10391"], - ["1274", "10399"], - ["1275", "10427"], - ["1276", "10429"], - ["1277", "10433"], - ["1278", "10453"], - ["1279", "10457"], - ["1280", "10459"], - ["1281", "10463"], - ["1282", "10477"], - ["1283", "10487"], - ["1284", "10499"], - ["1285", "10501"], - ["1286", "10513"], - ["1287", "10529"], - ["1288", "10531"], - ["1289", "10559"], - ["1290", "10567"], - ["1291", "10589"], - ["1292", "10597"], - ["1293", "10601"], - ["1294", "10607"], - ["1295", "10613"], - ["1296", "10627"], - ["1297", "10631"], - ["1298", "10639"], - ["1299", "10651"], - ["1300", "10657"], - ["1301", "10663"], - ["1302", "10667"], - ["1303", "10687"], - ["1304", "10691"], - ["1305", "10709"], - ["1306", "10711"], - ["1307", "10723"], - ["1308", "10729"], - ["1309", "10733"], - ["1310", "10739"], - ["1311", "10753"], - ["1312", "10771"], - ["1313", "10781"], - ["1314", "10789"], - ["1315", "10799"], - ["1316", "10831"], - ["1317", "10837"], - ["1318", "10847"], - ["1319", "10853"], - ["1320", "10859"], - ["1321", "10861"], - ["1322", "10867"], - ["1323", "10883"], - ["1324", "10889"], - ["1325", "10891"], - ["1326", "10903"], - ["1327", "10909"], - ["1328", "10937"], - ["1329", "10939"], - ["1330", "10949"], - ["1331", "10957"], - ["1332", "10973"], - ["1333", "10979"], - ["1334", "10987"], - ["1335", "10993"], - ["1336", "11003"], - ["1337", "11027"], - ["1338", "11047"], - ["1339", "11057"], - ["1340", "11059"], - ["1341", "11069"], - ["1342", "11071"], - ["1343", "11083"], - ["1344", "11087"], - ["1345", "11093"], - ["1346", "11113"], - ["1347", "11117"], - ["1348", "11119"], - ["1349", "11131"], - ["1350", "11149"], - ["1351", "11159"], - ["1352", "11161"], - ["1353", "11171"], - ["1354", "11173"], - ["1355", "11177"], - ["1356", "11197"], - ["1357", "11213"], - ["1358", "11239"], - ["1359", "11243"], - ["1360", "11251"], - ["1361", "11257"], - ["1362", "11261"], - ["1363", "11273"], - ["1364", "11279"], - ["1365", "11287"], - ["1366", "11299"], - ["1367", "11311"], - ["1368", "11317"], - ["1369", "11321"], - ["1370", "11329"], - ["1371", "11351"], - ["1372", "11353"], - ["1373", "11369"], - ["1374", "11383"], - ["1375", "11393"], - ["1376", "11399"], - ["1377", "11411"], - ["1378", "11423"], - ["1379", "11437"], - ["1380", "11443"], - ["1381", "11447"], - ["1382", "11467"], - ["1383", "11471"], - ["1384", "11483"], - ["1385", "11489"], - ["1386", "11491"], - ["1387", "11497"], - ["1388", "11503"], - ["1389", "11519"], - ["1390", "11527"], - ["1391", "11549"], - ["1392", "11551"], - ["1393", "11579"], - ["1394", "11587"], - ["1395", "11593"], - ["1396", "11597"], - ["1397", "11617"], - ["1398", "11621"], - ["1399", "11633"], - ["1400", "11657"], - ["1401", "11677"], - ["1402", "11681"], - ["1403", "11689"], - ["1404", "11699"], - ["1405", "11701"], - ["1406", "11717"], - ["1407", "11719"], - ["1408", "11731"], - ["1409", "11743"], - ["1410", "11777"], - ["1411", "11779"], - ["1412", "11783"], - ["1413", "11789"], - ["1414", "11801"], - ["1415", "11807"], - ["1416", "11813"], - ["1417", "11821"], - ["1418", "11827"], - ["1419", "11831"], - ["1420", "11833"], - ["1421", "11839"], - ["1422", "11863"], - ["1423", "11867"], - ["1424", "11887"], - ["1425", "11897"], - ["1426", "11903"], - ["1427", "11909"], - ["1428", "11923"], - ["1429", "11927"], - ["1430", "11933"], - ["1431", "11939"], - ["1432", "11941"], - ["1433", "11953"], - ["1434", "11959"], - ["1435", "11969"], - ["1436", "11971"], - ["1437", "11981"], - ["1438", "11987"], - ["1439", "12007"], - ["1440", "12011"], - ["1441", "12037"], - ["1442", "12041"], - ["1443", "12043"], - ["1444", "12049"], - ["1445", "12071"], - ["1446", "12073"], - ["1447", "12097"], - ["1448", "12101"], - ["1449", "12107"], - ["1450", "12109"], - ["1451", "12113"], - ["1452", "12119"], - ["1453", "12143"], - ["1454", "12149"], - ["1455", "12157"], - ["1456", "12161"], - ["1457", "12163"], - ["1458", "12197"], - ["1459", "12203"], - ["1460", "12211"], - ["1461", "12227"], - ["1462", "12239"], - ["1463", "12241"], - ["1464", "12251"], - ["1465", "12253"], - ["1466", "12263"], - ["1467", "12269"], - ["1468", "12277"], - ["1469", "12281"], - ["1470", "12289"], - ["1471", "12301"], - ["1472", "12323"], - ["1473", "12329"], - ["1474", "12343"], - ["1475", "12347"], - ["1476", "12373"], - ["1477", "12377"], - ["1478", "12379"], - ["1479", "12391"], - ["1480", "12401"], - ["1481", "12409"], - ["1482", "12413"], - ["1483", "12421"], - ["1484", "12433"], - ["1485", "12437"], - ["1486", "12451"], - ["1487", "12457"], - ["1488", "12473"], - ["1489", "12479"], - ["1490", "12487"], - ["1491", "12491"], - ["1492", "12497"], - ["1493", "12503"], - ["1494", "12511"], - ["1495", "12517"], - ["1496", "12527"], - ["1497", "12539"], - ["1498", "12541"], - ["1499", "12547"], - ["1500", "12553"], - ["1501", "12569"], - ["1502", "12577"], - ["1503", "12583"], - ["1504", "12589"], - ["1505", "12601"], - ["1506", "12611"], - ["1507", "12613"], - ["1508", "12619"], - ["1509", "12637"], - ["1510", "12641"], - ["1511", "12647"], - ["1512", "12653"], - ["1513", "12659"], - ["1514", "12671"], - ["1515", "12689"], - ["1516", "12697"], - ["1517", "12703"], - ["1518", "12713"], - ["1519", "12721"], - ["1520", "12739"], - ["1521", "12743"], - ["1522", "12757"], - ["1523", "12763"], - ["1524", "12781"], - ["1525", "12791"], - ["1526", "12799"], - ["1527", "12809"], - ["1528", "12821"], - ["1529", "12823"], - ["1530", "12829"], - ["1531", "12841"], - ["1532", "12853"], - ["1533", "12889"], - ["1534", "12893"], - ["1535", "12899"], - ["1536", "12907"], - ["1537", "12911"], - ["1538", "12917"], - ["1539", "12919"], - ["1540", "12923"], - ["1541", "12941"], - ["1542", "12953"], - ["1543", "12959"], - ["1544", "12967"], - ["1545", "12973"], - ["1546", "12979"], - ["1547", "12983"], - ["1548", "13001"], - ["1549", "13003"], - ["1550", "13007"], - ["1551", "13009"], - ["1552", "13033"], - ["1553", "13037"], - ["1554", "13043"], - ["1555", "13049"], - ["1556", "13063"], - ["1557", "13093"], - ["1558", "13099"], - ["1559", "13103"], - ["1560", "13109"], - ["1561", "13121"], - ["1562", "13127"], - ["1563", "13147"], - ["1564", "13151"], - ["1565", "13159"], - ["1566", "13163"], - ["1567", "13171"], - ["1568", "13177"], - ["1569", "13183"], - ["1570", "13187"], - ["1571", "13217"], - ["1572", "13219"], - ["1573", "13229"], - ["1574", "13241"], - ["1575", "13249"], - ["1576", "13259"], - ["1577", "13267"], - ["1578", "13291"], - ["1579", "13297"], - ["1580", "13309"], - ["1581", "13313"], - ["1582", "13327"], - ["1583", "13331"], - ["1584", "13337"], - ["1585", "13339"], - ["1586", "13367"], - ["1587", "13381"], - ["1588", "13397"], - ["1589", "13399"], - ["1590", "13411"], - ["1591", "13417"], - ["1592", "13421"], - ["1593", "13441"], - ["1594", "13451"], - ["1595", "13457"], - ["1596", "13463"], - ["1597", "13469"], - ["1598", "13477"], - ["1599", "13487"], - ["1600", "13499"], - ["1601", "13513"], - ["1602", "13523"], - ["1603", "13537"], - ["1604", "13553"], - ["1605", "13567"], - ["1606", "13577"], - ["1607", "13591"], - ["1608", "13597"], - ["1609", "13613"], - ["1610", "13619"], - ["1611", "13627"], - ["1612", "13633"], - ["1613", "13649"], - ["1614", "13669"], - ["1615", "13679"], - ["1616", "13681"], - ["1617", "13687"], - ["1618", "13691"], - ["1619", "13693"], - ["1620", "13697"], - ["1621", "13709"], - ["1622", "13711"], - ["1623", "13721"], - ["1624", "13723"], - ["1625", "13729"], - ["1626", "13751"], - ["1627", "13757"], - ["1628", "13759"], - ["1629", "13763"], - ["1630", "13781"], - ["1631", "13789"], - ["1632", "13799"], - ["1633", "13807"], - ["1634", "13829"], - ["1635", "13831"], - ["1636", "13841"], - ["1637", "13859"], - ["1638", "13873"], - ["1639", "13877"], - ["1640", "13879"], - ["1641", "13883"], - ["1642", "13901"], - ["1643", "13903"], - ["1644", "13907"], - ["1645", "13913"], - ["1646", "13921"], - ["1647", "13931"], - ["1648", "13933"], - ["1649", "13963"], - ["1650", "13967"], - ["1651", "13997"], - ["1652", "13999"], - ["1653", "14009"], - ["1654", "14011"], - ["1655", "14029"], - ["1656", "14033"], - ["1657", "14051"], - ["1658", "14057"], - ["1659", "14071"], - ["1660", "14081"], - ["1661", "14083"], - ["1662", "14087"], - ["1663", "14107"], - ["1664", "14143"], - ["1665", "14149"], - ["1666", "14153"], - ["1667", "14159"], - ["1668", "14173"], - ["1669", "14177"], - ["1670", "14197"], - ["1671", "14207"], - ["1672", "14221"], - ["1673", "14243"], - ["1674", "14249"], - ["1675", "14251"], - ["1676", "14281"], - ["1677", "14293"], - ["1678", "14303"], - ["1679", "14321"], - ["1680", "14323"], - ["1681", "14327"], - ["1682", "14341"], - ["1683", "14347"], - ["1684", "14369"], - ["1685", "14387"], - ["1686", "14389"], - ["1687", "14401"], - ["1688", "14407"], - ["1689", "14411"], - ["1690", "14419"], - ["1691", "14423"], - ["1692", "14431"], - ["1693", "14437"], - ["1694", "14447"], - ["1695", "14449"], - ["1696", "14461"], - ["1697", "14479"], - ["1698", "14489"], - ["1699", "14503"], - ["1700", "14519"], - ["1701", "14533"], - ["1702", "14537"], - ["1703", "14543"], - ["1704", "14549"], - ["1705", "14551"], - ["1706", "14557"], - ["1707", "14561"], - ["1708", "14563"], - ["1709", "14591"], - ["1710", "14593"], - ["1711", "14621"], - ["1712", "14627"], - ["1713", "14629"], - ["1714", "14633"], - ["1715", "14639"], - ["1716", "14653"], - ["1717", "14657"], - ["1718", "14669"], - ["1719", "14683"], - ["1720", "14699"], - ["1721", "14713"], - ["1722", "14717"], - ["1723", "14723"], - ["1724", "14731"], - ["1725", "14737"], - ["1726", "14741"], - ["1727", "14747"], - ["1728", "14753"], - ["1729", "14759"], - ["1730", "14767"], - ["1731", "14771"], - ["1732", "14779"], - ["1733", "14783"], - ["1734", "14797"], - ["1735", "14813"], - ["1736", "14821"], - ["1737", "14827"], - ["1738", "14831"], - ["1739", "14843"], - ["1740", "14851"], - ["1741", "14867"], - ["1742", "14869"], - ["1743", "14879"], - ["1744", "14887"], - ["1745", "14891"], - ["1746", "14897"], - ["1747", "14923"], - ["1748", "14929"], - ["1749", "14939"], - ["1750", "14947"], - ["1751", "14951"], - ["1752", "14957"], - ["1753", "14969"], - ["1754", "14983"], - ["1755", "15013"], - ["1756", "15017"], - ["1757", "15031"], - ["1758", "15053"], - ["1759", "15061"], - ["1760", "15073"], - ["1761", "15077"], - ["1762", "15083"], - ["1763", "15091"], - ["1764", "15101"], - ["1765", "15107"], - ["1766", "15121"], - ["1767", "15131"], - ["1768", "15137"], - ["1769", "15139"], - ["1770", "15149"], - ["1771", "15161"], - ["1772", "15173"], - ["1773", "15187"], - ["1774", "15193"], - ["1775", "15199"], - ["1776", "15217"], - ["1777", "15227"], - ["1778", "15233"], - ["1779", "15241"], - ["1780", "15259"], - ["1781", "15263"], - ["1782", "15269"], - ["1783", "15271"], - ["1784", "15277"], - ["1785", "15287"], - ["1786", "15289"], - ["1787", "15299"], - ["1788", "15307"], - ["1789", "15313"], - ["1790", "15319"], - ["1791", "15329"], - ["1792", "15331"], - ["1793", "15349"], - ["1794", "15359"], - ["1795", "15361"], - ["1796", "15373"], - ["1797", "15377"], - ["1798", "15383"], - ["1799", "15391"], - ["1800", "15401"], - ["1801", "15413"], - ["1802", "15427"], - ["1803", "15439"], - ["1804", "15443"], - ["1805", "15451"], - ["1806", "15461"], - ["1807", "15467"], - ["1808", "15473"], - ["1809", "15493"], - ["1810", "15497"], - ["1811", "15511"], - ["1812", "15527"], - ["1813", "15541"], - ["1814", "15551"], - ["1815", "15559"], - ["1816", "15569"], - ["1817", "15581"], - ["1818", "15583"], - ["1819", "15601"], - ["1820", "15607"], - ["1821", "15619"], - ["1822", "15629"], - ["1823", "15641"], - ["1824", "15643"], - ["1825", "15647"], - ["1826", "15649"], - ["1827", "15661"], - ["1828", "15667"], - ["1829", "15671"], - ["1830", "15679"], - ["1831", "15683"], - ["1832", "15727"], - ["1833", "15731"], - ["1834", "15733"], - ["1835", "15737"], - ["1836", "15739"], - ["1837", "15749"], - ["1838", "15761"], - ["1839", "15767"], - ["1840", "15773"], - ["1841", "15787"], - ["1842", "15791"], - ["1843", "15797"], - ["1844", "15803"], - ["1845", "15809"], - ["1846", "15817"], - ["1847", "15823"], - ["1848", "15859"], - ["1849", "15877"], - ["1850", "15881"], - ["1851", "15887"], - ["1852", "15889"], - ["1853", "15901"], - ["1854", "15907"], - ["1855", "15913"], - ["1856", "15919"], - ["1857", "15923"], - ["1858", "15937"], - ["1859", "15959"], - ["1860", "15971"], - ["1861", "15973"], - ["1862", "15991"], - ["1863", "16001"], - ["1864", "16007"], - ["1865", "16033"], - ["1866", "16057"], - ["1867", "16061"], - ["1868", "16063"], - ["1869", "16067"], - ["1870", "16069"], - ["1871", "16073"], - ["1872", "16087"], - ["1873", "16091"], - ["1874", "16097"], - ["1875", "16103"], - ["1876", "16111"], - ["1877", "16127"], - ["1878", "16139"], - ["1879", "16141"], - ["1880", "16183"], - ["1881", "16187"], - ["1882", "16189"], - ["1883", "16193"], - ["1884", "16217"], - ["1885", "16223"], - ["1886", "16229"], - ["1887", "16231"], - ["1888", "16249"], - ["1889", "16253"], - ["1890", "16267"], - ["1891", "16273"], - ["1892", "16301"], - ["1893", "16319"], - ["1894", "16333"], - ["1895", "16339"], - ["1896", "16349"], - ["1897", "16361"], - ["1898", "16363"], - ["1899", "16369"], - ["1900", "16381"], - ["1901", "16411"], - ["1902", "16417"], - ["1903", "16421"], - ["1904", "16427"], - ["1905", "16433"], - ["1906", "16447"], - ["1907", "16451"], - ["1908", "16453"], - ["1909", "16477"], - ["1910", "16481"], - ["1911", "16487"], - ["1912", "16493"], - ["1913", "16519"], - ["1914", "16529"], - ["1915", "16547"], - ["1916", "16553"], - ["1917", "16561"], - ["1918", "16567"], - ["1919", "16573"], - ["1920", "16603"], - ["1921", "16607"], - ["1922", "16619"], - ["1923", "16631"], - ["1924", "16633"], - ["1925", "16649"], - ["1926", "16651"], - ["1927", "16657"], - ["1928", "16661"], - ["1929", "16673"], - ["1930", "16691"], - ["1931", "16693"], - ["1932", "16699"], - ["1933", "16703"], - ["1934", "16729"], - ["1935", "16741"], - ["1936", "16747"], - ["1937", "16759"], - ["1938", "16763"], - ["1939", "16787"], - ["1940", "16811"], - ["1941", "16823"], - ["1942", "16829"], - ["1943", "16831"], - ["1944", "16843"], - ["1945", "16871"], - ["1946", "16879"]]}}]} -================================================================================ diff --git a/modules/services/serviceAi/subAiCallLooping-flow.md b/modules/services/serviceAi/subAiCallLooping-flow.md new file mode 100644 index 00000000..e68b2f3c --- /dev/null +++ b/modules/services/serviceAi/subAiCallLooping-flow.md @@ -0,0 +1,237 @@ +# AI Call Iteration Flow - JSON Merging System + +This document describes the iteration flow for handling large JSON responses from AI that may be truncated and need to be merged across multiple iterations. + +## Overview + +When an AI response is too large, it may be truncated (cut) at an arbitrary point. The iteration system: +1. Detects incomplete JSON +2. Requests continuation from the AI +3. Merges the continuation with the existing JSON +4. Repeats until complete or max failures reached + +--- + +## Key Variables + +| Variable | Type | Purpose | +|----------|------|---------| +| `jsonBase` | `str \| None` | The merged JSON string (CUT version for overlap matching) | +| `candidateJson` | `str` | Temporary holder for merged result until validated | +| `lastValidCompletePart` | `str \| None` | Fallback - last successfully parsed CLOSED JSON | +| `lastOverlapContext` | `str` | Context for retry/continuation prompts | +| `lastHierarchyContextForPrompt` | `str` | Context for retry/continuation prompts | +| `mergeFailCount` | `int` | Global counter (max 3 failures) | + +--- + +## Key Distinction: hierarchyContext vs completePart + +| Field | Description | Use Case | +|-------|-------------|----------| +| `hierarchyContext` | **CUT JSON** - truncated at cut point | Used as `jsonBase` for merging with next AI fragment | +| `completePart` | **CLOSED JSON** - all structures properly closed | Used for validation, parsing, and fallback | + +**Why this matters:** +- The next AI fragment starts with an **overlap** that matches the CUT point +- If we used `completePart` (closed), the overlap detection would FAIL +- We must use `hierarchyContext` (cut) so overlap matching works correctly + +--- + +## Flow Steps + +### Step 1: BUILD PROMPT + +**Location:** `subAiCallLooping.py` lines 163-212 +**Function:** `buildContinuationContext()` from `modules/shared/jsonUtils.py` + +- **First iteration:** Use original prompt +- **Continuation:** `buildContinuationContext(allSections, lastRawResponse, ...)` + - Internally calls `getContexts(lastRawResponse)` to get overlap/hierarchy + - Builds continuation prompt with `overlapContext` + `hierarchyContextForPrompt` + +### Step 2: CALL AI + +**Location:** `subAiCallLooping.py` lines 214-299 +**Function:** `self.aiService.callAi(request)` + +- Returns `response.content` as `result` +- Store raw response: `lastRawResponse = result` + +### Step 4: MERGE + +**Location:** `subAiCallLooping.py` lines 338-396 +**Function:** `JsonResponseHandler.mergeJsonStringsWithOverlap()` from `modules/services/serviceAi/subJsonResponseHandling.py` + +``` +IF first iteration (jsonBase is None): + → candidateJson = result +ELSE: + → mergedJsonString, hasOverlap = mergeJsonStringsWithOverlap(jsonBase, result) + + IF hasOverlap = False (MERGE FAILED): + → mergeFailCount++ + → If mergeFailCount >= 3: return lastValidCompletePart (fallback) + → Else: continue (retry with unchanged jsonBase) + ELSE: + → candidateJson = mergedJsonString (don't update jsonBase yet!) + +TRY DIRECT PARSE of candidateJson: + IF parse succeeds: + → jsonBase = candidateJson (commit) + → FINISHED! Return normalized result + ELSE: + → Proceed to Step 5 +``` + +### Step 5: GET CONTEXTS + +**Location:** `subAiCallLooping.py` lines 420-427 +**Function:** `getContexts()` from `modules/shared/jsonContinuation.py` + +```python +contexts = getContexts(candidateJson) +``` + +Returns `JsonContinuationContexts`: +- `overlapContext`: `""` if JSON is complete (no cut point) +- `hierarchyContext`: CUT JSON (for merging with next fragment) +- `hierarchyContextForPrompt`: CUT JSON with budget limits (for prompts) +- `completePart`: CLOSED JSON (repaired if needed) +- `jsonParsingSuccess`: `True` if completePart is valid JSON + +**Enhancement:** If original JSON is already complete → `overlapContext = ""` +This signals "JSON is complete, no more continuation needed" + +### Step 6: DECIDE + +**Location:** `subAiCallLooping.py` lines 429-528 + +#### Case A: `jsonParsingSuccess=true` AND `overlapContext=""` +**→ FINISHED** +- JSON is complete (no cut point) +- `jsonBase = contexts.completePart` (use CLOSED version for final result) +- Return `completePart` as result + +#### Case B: `jsonParsingSuccess=true` AND `overlapContext!=""` +**→ CONTINUE to next iteration** +- JSON parseable but has cut point +- `jsonBase = contexts.hierarchyContext` ← **CUT version for next merge!** +- `lastValidCompletePart = contexts.completePart` ← **CLOSED version for fallback** +- Store contexts for next prompt +- `mergeFailCount = 0` (reset on success) +- `lastRawResponse = jsonBase` +- Continue to next iteration + +#### Case C: `jsonParsingSuccess=false` +**→ RETRY with same prompt** +- Do NOT update `jsonBase` (keep previous valid state) +- `mergeFailCount++` +- If `mergeFailCount >= 3`: return `lastValidCompletePart` (fallback) +- Else: continue (retry with unchanged jsonBase/lastRawResponse) + +--- + +## Flow Diagram + +``` + ┌───────────────────────────────────────────────────────────────┐ + │ ITERATION START │ + └───────────────────────────┬───────────────────────────────────┘ + │ + ┌───────────────────────────▼───────────────────────────────────┐ + │ STEP 1: BUILD PROMPT │ + │ - First: original prompt │ + │ - Next: buildContinuationContext(lastRawResponse) │ + └───────────────────────────┬───────────────────────────────────┘ + │ + ┌───────────────────────────▼───────────────────────────────────┐ + │ STEP 2: CALL AI → result │ + └───────────────────────────┬───────────────────────────────────┘ + │ + ┌───────────────────────────▼───────────────────────────────────┐ + │ STEP 4: MERGE jsonBase + result → candidateJson │ + └───────────────────────────┬───────────────────────────────────┘ + │ + ┌────────────▼────────────┐ + │ Merge OK? │ + └────────────┬────────────┘ + │ + ┌─────────────────────┼─────────────────────┐ + │ NO │ YES │ + ▼ ▼ │ + ┌──────────────┐ ┌──────────────────┐ │ + │ fails++ │ │ TRY DIRECT PARSE │ │ + │ if >=3: │ │ of candidateJson │ │ + │ RETURN │ └────────┬─────────┘ │ + │ fallback │ │ │ + │ else: RETRY │ ┌────────▼─────────┐ │ + │ (continue) │ │ Parse OK? │ │ + └──────────────┘ └────────┬─────────┘ │ + │ │ + ┌─────────────────────┼─────────────────────┐ + │ YES │ NO │ + ▼ ▼ │ + ┌──────────────┐ ┌──────────────────────────────┐ + │ FINISHED ✓ │ │ STEP 5: getContexts() │ + │ Return │ │ → jsonParsingSuccess │ + │ normalized │ │ → overlapContext │ + │ result │ └────────────┬─────────────────┘ + └──────────────┘ │ + ┌────────────▼────────────────────┐ + │ STEP 6: DECIDE │ + └────────────┬────────────────────┘ + │ + ┌────────────────────────────┼────────────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌───────────────────┐ ┌───────────────────────┐ ┌───────────────────┐ +│ success=true │ │ success=true │ │ success=false │ +│ overlap="" │ │ overlap!="" │ │ │ +│ ───────────── │ │ ───────────────── │ │ ───────────── │ +│ FINISHED ✓ │ │ CONTINUE │ │ RETRY │ +│ │ │ │ │ │ +│ jsonBase = │ │ jsonBase = │ │ jsonBase unchanged│ +│ completePart │ │ hierarchyContext │ │ fails++ │ +│ (CLOSED) │ │ (CUT for merge!) │ │ │ +│ │ │ │ │ if >=3: fallback │ +│ Return result │ │ fallback = │ │ else: retry │ +│ │ │ completePart │ │ │ +│ │ │ (CLOSED) │ │ │ +│ │ │ │ │ │ +│ │ │ Next iteration → │ │ │ +└───────────────────┘ └───────────────────────┘ └───────────────────┘ +``` + +--- + +## Files Involved + +| File | Purpose | +|------|---------| +| `modules/services/serviceAi/subAiCallLooping.py` | Main iteration loop | +| `modules/shared/jsonContinuation.py` | `getContexts()` - context extraction & repair | +| `modules/shared/jsonUtils.py` | `buildContinuationContext()` - prompt building | +| `modules/services/serviceAi/subJsonResponseHandling.py` | `mergeJsonStringsWithOverlap()` | +| `modules/services/serviceAi/subJsonMerger.py` | `ModularJsonMerger` - actual merge logic | +| `modules/datamodels/datamodelAi.py` | `JsonContinuationContexts` model | + +--- + +## Error Handling + +### Merge Failures +- Max 3 consecutive failures allowed +- On failure: retry with unchanged `jsonBase` (previous valid state) +- After 3 failures: return `lastValidCompletePart` as fallback + +### Parse Failures +- If `getContexts()` cannot produce valid JSON: increment fail counter +- Retry with same prompt (don't update jsonBase) +- After 3 failures: return `lastValidCompletePart` as fallback + +### Fallback Strategy +- `lastValidCompletePart` stores the last successfully parsed CLOSED JSON +- Always available as fallback when things go wrong +- Ensures we return valid JSON even after multiple failures diff --git a/modules/services/serviceAi/subAiCallLooping.py b/modules/services/serviceAi/subAiCallLooping.py index 63051d8b..31cca318 100644 --- a/modules/services/serviceAi/subAiCallLooping.py +++ b/modules/services/serviceAi/subAiCallLooping.py @@ -7,7 +7,44 @@ Handles AI calls with looping and repair logic, including: - Looping with JSON repair and continuation - KPI definition and tracking - Progress tracking and iteration management + +FLOW LOGIC + +VARIABLES: +- jsonBase: str (merged JSON so far, starts empty) +- lastValidCompletePart: str (fallback for failures) +- mergeFailCount: int = 0 (max 3) + +FLOW: +┌─────────────────────────────────────────────────────────────────┐ +│ 1. BUILD PROMPT │ +│ - First: original prompt │ +│ - Next: buildContinuationContext(lastRawResponse) │ +├─────────────────────────────────────────────────────────────────┤ +│ 2. CALL AI → response fragment │ +├─────────────────────────────────────────────────────────────────┤ +│ 4. MERGE jsonBase + response │ +│ ├─ FAILS: repeat prompt, fails++ (if >=3 return fallback) │ +│ └─ SUCCEEDS: try parse │ +│ ├─ SUCCEEDS: FINISHED │ +│ └─ FAILS: → step 5 │ +├─────────────────────────────────────────────────────────────────┤ +│ 5. GET CONTEXTS (merge OK, parse failed) │ +│ getContexts(mergedJson) → │ +│ - If no cut point: overlapContext = "" │ +│ - Store contexts for next iteration │ +├─────────────────────────────────────────────────────────────────┤ +│ 6. DECIDE │ +│ ├─ jsonParsingSuccess=true AND overlapContext="": │ +│ │ FINISHED. return completePart │ +│ ├─ jsonParsingSuccess=true AND overlapContext!="": │ +│ │ CONTINUE, fails=0 │ +│ └─ ELSE: repeat prompt, fails++ │ +└─────────────────────────────────────────────────────────────────┘ + + """ + import json import logging from typing import Dict, Any, List, Optional, Callable @@ -16,10 +53,14 @@ from modules.datamodels.datamodelAi import ( AiCallRequest, AiCallOptions ) from modules.datamodels.datamodelExtraction import ContentPart -from modules.shared.jsonUtils import buildContinuationContext, extractJsonString, tryParseJson from modules.services.serviceAi.subJsonResponseHandling import JsonResponseHandler from modules.services.serviceAi.subLoopingUseCases import LoopingUseCaseRegistry from modules.workflows.processing.shared.stateTools import checkWorkflowStopped +from modules.shared.jsonContinuation import getContexts +from modules.shared.jsonUtils import buildContinuationContext, extractJsonString, tryParseJson +from modules.shared.jsonUtils import tryParseJson +from modules.shared.jsonUtils import closeJsonStructures +from modules.shared.jsonUtils import stripCodeFences, normalizeJsonText logger = logging.getLogger(__name__) @@ -88,7 +129,18 @@ class AiCallLooper: iteration = 0 allSections = [] # Accumulate all sections across iterations lastRawResponse = None # Store last raw JSON response for continuation - accumulatedDirectJson = [] # Accumulate JSON strings for direct return use cases (chapter_structure, code_structure) + + # JSON Base Iteration System: + # - jsonBase: the merged JSON string (replaces accumulatedDirectJson array) + # - After each iteration, new response is merged with jsonBase + # - On merge success: check if complete, store contexts for next iteration + # - On merge fail: retry with same prompt, increment fails + jsonBase = None # Merged JSON string (starts None, set on first response) + + # Merge fail tracking - stop after 3 consecutive merge failures + MAX_MERGE_FAILS = 3 + mergeFailCount = 0 # Global counter for merge failures across entire loop + lastValidCompletePart = None # Store last successfully parsed completePart for fallback # Get parent operation ID for iteration operations (parentId should be operationId, not log entry ID) parentOperationId = operationId # Use the parent's operationId directly @@ -136,6 +188,17 @@ class AiCallLooper: if not lastRawResponse: logger.warning(f"Iteration {iteration}: No previous response available for continuation!") + # Store valid completePart from continuation context for fallback on merge failures + # Use getContexts to check if completePart is parseable and store it + if lastRawResponse and not lastValidCompletePart: + try: + contexts = getContexts(lastRawResponse) + if contexts.jsonParsingSuccess and contexts.completePart: + lastValidCompletePart = contexts.completePart + logger.debug(f"Iteration {iteration}: Stored initial valid completePart ({len(lastValidCompletePart)} chars)") + except Exception as e: + logger.debug(f"Iteration {iteration}: Failed to extract completePart: {e}") + # Unified prompt builder call: Continuation builders only need continuationContext, templateStructure, and basePrompt # All initial context (section, userPrompt, etc.) is already in basePrompt, so promptArgs is not needed # Extract templateStructure and basePrompt from promptArgs (they're explicit parameters) @@ -247,238 +310,220 @@ class AiCallLooper: # Store raw response for continuation (even if broken) lastRawResponse = result - # Parse JSON for use case handling - parsedJsonForUseCase = None - extractedJsonForUseCase = None - - try: - extractedJsonForUseCase = extractJsonString(result) - parsedJson, parseError, _ = tryParseJson(extractedJsonForUseCase) - if parseError is None and parsedJson: - parsedJsonForUseCase = parsedJson - except Exception: - pass - # Handle use cases that return JSON directly (no section extraction needed) # Check if use case supports direct return (all registered use cases do) if useCase and not useCase.requiresExtraction: - # For all direct return use cases, check completeness and support looping - if True: # All registered use cases support looping - # If parsing failed (e.g., invalid JSON with comments or truncated JSON), continue looping to get valid JSON - if not parsedJsonForUseCase: - logger.info(f"Iteration {iteration}: Use case '{useCaseId}' - JSON parsing failed (likely incomplete/truncated), continuing iteration to complete") - # Accumulate response for merging in next iteration - accumulatedDirectJson.append(result) + # ===================================================================== + # ITERATION FLOW (Simplified) + # ===================================================================== + # Step 4: MERGE jsonBase + new response + # - FAILS: repeat prompt, increment fails cont (if >=3 return fallback) + # - SUCCEEDS: try parse + # - SUCCEEDS: FINISHED + # - FAILS: proceed to Step 5 + # Step 5: GET CONTEXTS (merge OK, parse failed) + # - getContexts() with repair + # - If no cut point: overlapContext = "" + # Step 6: DECIDE + # - jsonParsingSuccess=true AND overlapContext="": FINISHED + # - jsonParsingSuccess=true AND overlapContext!="": continue, fails=0 + # - ELSE: repeat prompt, increment fails count + # ===================================================================== + + # STEP 4: MERGE jsonBase + new response + # Use candidateJson to hold merged result until we confirm it's valid + candidateJson = None + + if jsonBase is None: + # First iteration - candidate is the current result + candidateJson = result + logger.debug(f"Iteration {iteration}: First response, candidateJson ({len(candidateJson)} chars)") + else: + # Merge jsonBase with new response + logger.info(f"Iteration {iteration}: Merging jsonBase ({len(jsonBase)} chars) with new response ({len(result)} chars)") + mergedJsonString, hasOverlap = JsonResponseHandler.mergeJsonStringsWithOverlap(jsonBase, result) + + if not hasOverlap: + # MERGE FAILED - repeat prompt with unchanged jsonBase + mergeFailCount += 1 + logger.warning( + f"Iteration {iteration}: Merge failed, no overlap found " + f"(fail {mergeFailCount}/{MAX_MERGE_FAILS})" + ) - # Continue to next iteration - continuation prompt builder will handle the rest - if iterationOperationId: - self.services.chat.progressLogUpdate(iterationOperationId, 0.7, "JSON incomplete, requesting continuation") - self.services.chat.progressLogFinish(iterationOperationId, True) - continue - - # If we successfully parsed JSON, check completeness using parsed structure - # CRITICAL: If parsing succeeded, trust the parsed JSON structure check - # The string-based check can have false positives on valid JSON (e.g., due to normalization issues) - # Only use string-based check when parsing fails (already handled above) - isComplete = JsonResponseHandler.isJsonComplete(parsedJsonForUseCase) - - # If parsed check says complete, trust it - don't override with string check - # String check is only reliable when parsing fails (truncated JSON that gets closed for parsing) - # For successfully parsed JSON, the structure check is definitive - - if not isComplete: - logger.warning(f"Iteration {iteration}: Use case '{useCaseId}' - JSON is incomplete, continuing for continuation") - # Accumulate response for merging in next iteration - accumulatedDirectJson.append(result) - - # Continue to next iteration - continuation prompt builder will handle the rest - if iterationOperationId: - self.services.chat.progressLogUpdate(iterationOperationId, 0.7, "JSON incomplete, requesting continuation") - self.services.chat.progressLogFinish(iterationOperationId, True) - continue - else: - # JSON is complete - merge accumulated responses if any - if accumulatedDirectJson: - logger.info(f"Iteration {iteration}: Merging {len(accumulatedDirectJson) + 1} accumulated responses") + if mergeFailCount >= MAX_MERGE_FAILS: + # Max failures reached - return last valid completePart + logger.error( + f"Iteration {iteration}: Max merge failures ({MAX_MERGE_FAILS}) reached, " + "returning last valid completePart" + ) + if iterationOperationId: + self.services.chat.progressLogFinish(iterationOperationId, False) - # Use generic data-based merging for all use cases - try: - # Strategy: Merge strings first for incomplete JSON, then parse and merge parsed objects - # This ensures incomplete JSON from part 1 is preserved - allJsonStrings = accumulatedDirectJson + [result] - - # Step 1: Merge all JSON strings using existing overlap detection - mergedJsonString = allJsonStrings[0] if allJsonStrings else "" - hasOverlap = True # Track if any overlap was found - for jsonStr in allJsonStrings[1:]: - mergedJsonString, hasOverlapInMerge = JsonResponseHandler.mergeJsonStringsWithOverlap(mergedJsonString, jsonStr) - # If no overlap found in any merge, stop iterations - if not hasOverlapInMerge: - hasOverlap = False - logger.info(f"Iteration {iteration}: No overlap found during merge - stopping iterations and closing JSON") - break - - # If no overlap was found, mark as complete and use closed JSON - if not hasOverlap: - isComplete = True - # JSON is already closed by mergeJsonStringsWithOverlap when no overlap - # Use the merged (closed) JSON string directly - result = mergedJsonString - # CRITICAL: Update lastRawResponse with merged result for next iteration - lastRawResponse = mergedJsonString - # Try to parse it to get parsedJsonForUseCase - try: - extracted = extractJsonString(mergedJsonString) - parsed, parseErr, _ = tryParseJson(extracted) - if parseErr is None and parsed: - # Use callback to normalize JSON structure - normalized = self._normalizeJsonStructure(parsed, useCase) - parsedJsonForUseCase = normalized - result = json.dumps(normalized, indent=2, ensure_ascii=False) - # CRITICAL: Update lastRawResponse with final result - lastRawResponse = result - else: - # Parsing failed - try to repair JSON - from modules.shared.jsonUtils import repairBrokenJson - logger.warning( - f"Iteration {iteration}: JSON parse failed after no-overlap merge, " - f"attempting repair: {str(parseErr) if parseErr else 'Unknown error'}" - ) - repairedJson = repairBrokenJson(extracted) - if repairedJson and isinstance(repairedJson, dict): - # repairBrokenJson returns a dict directly - use it - normalized = self._normalizeJsonStructure(repairedJson, useCase) - parsedJsonForUseCase = normalized - result = json.dumps(normalized, indent=2, ensure_ascii=False) - # CRITICAL: Update lastRawResponse with final result - lastRawResponse = result - logger.info(f"Iteration {iteration}: Successfully repaired JSON after no-overlap merge") - except Exception as e: - # Last resort: try repair on the original merged string - logger.warning( - f"Iteration {iteration}: Exception during no-overlap JSON processing, " - f"attempting repair: {str(e)}" - ) - try: - from modules.shared.jsonUtils import repairBrokenJson - repairedJson = repairBrokenJson(mergedJsonString) - if repairedJson and isinstance(repairedJson, dict): - normalized = self._normalizeJsonStructure(repairedJson, useCase) - parsedJsonForUseCase = normalized - result = json.dumps(normalized, indent=2, ensure_ascii=False) - logger.info(f"Iteration {iteration}: Successfully repaired JSON after exception") - else: - logger.error(f"Iteration {iteration}: JSON repair failed, using string result as-is") - except Exception as repairError: - logger.error( - f"Iteration {iteration}: JSON repair also failed: {str(repairError)}, " - "using string result as-is" - ) - else: - # Overlap found - continue with normal processing - # Step 2: Try to parse the merged string - extracted = extractJsonString(mergedJsonString) - parsed, parseErr, _ = tryParseJson(extracted) - - if parseErr is None and parsed: - # Parsing succeeded - normalize and use (via callback) - normalized = self._normalizeJsonStructure(parsed, useCase) - parsedJsonForUseCase = normalized - result = json.dumps(normalized, indent=2, ensure_ascii=False) - # CRITICAL: Update lastRawResponse with merged result - lastRawResponse = result - else: - # Parsing failed - try to extract partial data using Deep-Structure-Merging - # This fallback works for all use cases: parse what we can from each part - allParsed = [] - for jsonStr in allJsonStrings: - extracted = extractJsonString(jsonStr) - parsed, parseErr, _ = tryParseJson(extracted) - if parseErr is None and parsed: - # Use callback to normalize JSON structure - normalized = self._normalizeJsonStructure(parsed, useCase) - allParsed.append(normalized) - - if allParsed: - # Use mergeDeepStructures for intelligent merging across all use cases - if len(allParsed) > 1: - mergedJsonObj = allParsed[0] - for nextObj in allParsed[1:]: - mergedJsonObj = JsonResponseHandler.mergeDeepStructures( - mergedJsonObj, nextObj, iteration, f"{useCaseId}.merge" - ) - else: - mergedJsonObj = allParsed[0] - - parsedJsonForUseCase = mergedJsonObj - result = json.dumps(mergedJsonObj, indent=2, ensure_ascii=False) - # CRITICAL: Update lastRawResponse with merged result - lastRawResponse = result - else: - # All parsing failed - use string merge result - result = mergedJsonString - # CRITICAL: Update lastRawResponse with merged result - lastRawResponse = mergedJsonString - except Exception as e: - logger.warning(f"Failed data-based merge, falling back to string merging: {e}") - # Fallback to string merging - mergedJsonString = accumulatedDirectJson[0] if accumulatedDirectJson else result - hasOverlap = True # Track if any overlap was found - for prevJson in accumulatedDirectJson[1:]: - mergedJsonString, hasOverlapInMerge = JsonResponseHandler.mergeJsonStringsWithOverlap(mergedJsonString, prevJson) - if not hasOverlapInMerge: - hasOverlap = False - logger.info(f"Iteration {iteration}: No overlap found during fallback merge - stopping iterations") - break - if hasOverlap: - mergedJsonString, hasOverlapInMerge = JsonResponseHandler.mergeJsonStringsWithOverlap(mergedJsonString, result) - if not hasOverlapInMerge: - hasOverlap = False - logger.info(f"Iteration {iteration}: No overlap found in final fallback merge - stopping iterations") - result = mergedJsonString - # CRITICAL: Update lastRawResponse with merged result - lastRawResponse = mergedJsonString - - # If no overlap was found, mark as complete and use closed JSON - if not hasOverlap: - isComplete = True - # JSON is already closed by mergeJsonStringsWithOverlap when no overlap - # Try to parse it to get parsedJsonForUseCase - try: - extractedMerged = extractJsonString(result) - parsedMerged, parseError, _ = tryParseJson(extractedMerged) - if parseError is None and parsedMerged: - parsedJsonForUseCase = parsedMerged - except Exception: - pass # Use string result if parsing fails - - # Try to parse the string-merged result + if lastValidCompletePart: try: - extractedMerged = extractJsonString(result) - parsedMerged, parseError, _ = tryParseJson(extractedMerged) - if parseError is None and parsedMerged: - parsedJsonForUseCase = parsedMerged + extracted = extractJsonString(lastValidCompletePart) + parsed, parseErr, _ = tryParseJson(extracted) + if parseErr is None and parsed: + normalized = self._normalizeJsonStructure(parsed, useCase) + return json.dumps(normalized, indent=2, ensure_ascii=False) except Exception: pass + return lastValidCompletePart + else: + # No valid fallback - return whatever we have + return jsonBase if jsonBase else "" - logger.info(f"Iteration {iteration}: Use case '{useCaseId}' - JSON is complete") + # Not at max failures - retry with same prompt (jsonBase unchanged) + if iterationOperationId: + self.services.chat.progressLogUpdate( + iterationOperationId, 0.7, + f"Merge failed ({mergeFailCount}/{MAX_MERGE_FAILS}), retrying" + ) + self.services.chat.progressLogFinish(iterationOperationId, True) + continue + + # MERGE SUCCEEDED - set candidate (don't update jsonBase yet!) + candidateJson = mergedJsonString + logger.debug(f"Iteration {iteration}: Merge succeeded, candidateJson ({len(candidateJson)} chars)") - logger.info(f"Iteration {iteration}: Use case '{useCaseId}' - returning JSON directly") - if iterationOperationId: - self.services.chat.progressLogFinish(iterationOperationId, True) + # Try direct parse of candidate + try: + extracted = extractJsonString(candidateJson) + parsed, parseErr, _ = tryParseJson(extracted) + if parseErr is None and parsed: + # Direct parse succeeded - FINISHED + # Commit candidate to jsonBase + jsonBase = candidateJson + logger.info(f"Iteration {iteration}: Direct parse succeeded, JSON is complete") + normalized = self._normalizeJsonStructure(parsed, useCase) + result = json.dumps(normalized, indent=2, ensure_ascii=False) + + if iterationOperationId: + self.services.chat.progressLogFinish(iterationOperationId, True) + + if not useCase.finalResultHandler: + raise ValueError( + f"Use case '{useCaseId}' is missing required 'finalResultHandler' callback." + ) + return useCase.finalResultHandler( + result, normalized, extracted, debugPrefix, self.services + ) + except Exception as e: + logger.debug(f"Iteration {iteration}: Direct parse failed: {e}") - # Use callback to handle final result formatting and debug file writing (REQUIRED - no fallback) - if not useCase.finalResultHandler: - raise ValueError( - f"Use case '{useCaseId}' is missing required 'finalResultHandler' callback. " - "All use cases must provide a finalResultHandler function." - ) - final_json = useCase.finalResultHandler( - result, parsedJsonForUseCase, extractedJsonForUseCase, - debugPrefix, self.services + # STEP 5: GET CONTEXTS (merge OK, parse failed = cut JSON) + # Use candidateJson for context extraction + contexts = getContexts(candidateJson) + logger.debug( + f"Iteration {iteration}: getContexts() -> " + f"jsonParsingSuccess={contexts.jsonParsingSuccess}, " + f"overlapContext={'\"\"' if not contexts.overlapContext else f'({len(contexts.overlapContext)} chars)'}" ) - return final_json + # STEP 6: DECIDE based on jsonParsingSuccess and overlapContext + if contexts.jsonParsingSuccess and contexts.overlapContext == "": + # JSON is complete (no cut point) - FINISHED + # Use completePart for final result (closed, repaired JSON) + # No more merging needed, so we don't need the cut version + jsonBase = contexts.completePart + logger.info(f"Iteration {iteration}: jsonParsingSuccess=true, overlapContext='', JSON complete") + + # Store and parse completePart + lastValidCompletePart = contexts.completePart + + try: + extracted = extractJsonString(contexts.completePart) + parsed, parseErr, _ = tryParseJson(extracted) + if parseErr is None and parsed: + normalized = self._normalizeJsonStructure(parsed, useCase) + result = json.dumps(normalized, indent=2, ensure_ascii=False) + + if iterationOperationId: + self.services.chat.progressLogFinish(iterationOperationId, True) + + if not useCase.finalResultHandler: + raise ValueError( + f"Use case '{useCaseId}' is missing required 'finalResultHandler' callback." + ) + return useCase.finalResultHandler( + result, normalized, extracted, debugPrefix, self.services + ) + except Exception as e: + logger.warning(f"Iteration {iteration}: Failed to parse completePart: {e}") + + # Fallback: return completePart as-is + if iterationOperationId: + self.services.chat.progressLogFinish(iterationOperationId, True) + return contexts.completePart + + elif contexts.jsonParsingSuccess and contexts.overlapContext != "": + # JSON parseable but has cut point - CONTINUE to next iteration + # CRITICAL: Use hierarchyContext (CUT json) as jsonBase for next merge! + # - hierarchyContext = the truncated JSON at cut point (needed for overlap matching) + # - completePart = closed JSON (for validation/fallback only) + # The next AI fragment's overlap must match the CUT point, not closed structures + jsonBase = contexts.hierarchyContext + logger.info( + f"Iteration {iteration}: jsonParsingSuccess=true, overlapContext not empty, " + f"continuing iteration (jsonBase updated to hierarchyContext: {len(jsonBase)} chars)" + ) + + # Store valid completePart as fallback (different from jsonBase!) + lastValidCompletePart = contexts.completePart + + # Reset fail counter on successful progress + mergeFailCount = 0 + + # Update lastRawResponse for continuation prompt building + # Use the CUT version for prompt context as well + lastRawResponse = jsonBase + + if iterationOperationId: + self.services.chat.progressLogUpdate(iterationOperationId, 0.7, "JSON incomplete, requesting continuation") + self.services.chat.progressLogFinish(iterationOperationId, True) + continue + + else: + # JSON not parseable after repair - repeat prompt, increment fails + # Do NOT update jsonBase - keep previous valid state + mergeFailCount += 1 + logger.warning( + f"Iteration {iteration}: jsonParsingSuccess=false, " + f"repeat prompt (fail {mergeFailCount}/{MAX_MERGE_FAILS})" + ) + + if mergeFailCount >= MAX_MERGE_FAILS: + # Max failures reached - return last valid completePart + logger.error( + f"Iteration {iteration}: Max failures ({MAX_MERGE_FAILS}) reached, " + "returning last valid completePart" + ) + if iterationOperationId: + self.services.chat.progressLogFinish(iterationOperationId, False) + + if lastValidCompletePart: + try: + extracted = extractJsonString(lastValidCompletePart) + parsed, parseErr, _ = tryParseJson(extracted) + if parseErr is None and parsed: + normalized = self._normalizeJsonStructure(parsed, useCase) + return json.dumps(normalized, indent=2, ensure_ascii=False) + except Exception: + pass + return lastValidCompletePart + else: + return jsonBase if jsonBase else "" + + # Not at max - retry with same prompt + # Do NOT update jsonBase or lastRawResponse - keep previous for retry + if iterationOperationId: + self.services.chat.progressLogUpdate( + iterationOperationId, 0.7, + f"Parse failed ({mergeFailCount}/{MAX_MERGE_FAILS}), retrying" + ) + self.services.chat.progressLogFinish(iterationOperationId, True) + continue except Exception as e: logger.error(f"Error in AI call iteration {iteration}: {str(e)}") @@ -510,9 +555,7 @@ class AiCallLooper: """ if not jsonString or not jsonString.strip(): return False - - from modules.shared.jsonUtils import stripCodeFences, normalizeJsonText - + # Normalize JSON string normalized = stripCodeFences(normalizeJsonText(jsonString)).strip() if not normalized: @@ -587,14 +630,12 @@ class AiCallLooper: # - Ends with incomplete object property (e.g., {"key": "val) # If JSON parses successfully without closing, it's complete - from modules.shared.jsonUtils import tryParseJson parsed, parseErr, _ = tryParseJson(jsonContent) if parseErr is None: # Parses successfully - it's complete return False # If it doesn't parse, try closing it and see if that helps - from modules.shared.jsonUtils import closeJsonStructures closed = closeJsonStructures(jsonContent) parsedClosed, parseErrClosed, _ = tryParseJson(closed) diff --git a/modules/shared/BUDGET_RENDERING_REQUIREMENT.md b/modules/shared/BUDGET_RENDERING_REQUIREMENT.md deleted file mode 100644 index 6e9afc7d..00000000 --- a/modules/shared/BUDGET_RENDERING_REQUIREMENT.md +++ /dev/null @@ -1,169 +0,0 @@ -# Budget Rendering Requirement for JSON Continuation Context - -## Problem Statement -When rendering the `hierarchyContextForPrompt` with budget constraints, we need to prioritize data near the cut point (where JSON was truncated) over data near the root. Currently, the implementation renders top-down (root → children), causing root-level data to consume budget before cut-level data gets rendered. - -## Requirement - -### Core Logic -1. **Find the cut element**: Identify the element in the JSON structure where truncation occurred (the cut point). - -2. **Build path from cut to root**: Create an ordered list of nodes from the cut element back to the root element. The path should be: `[cut_element, parent_of_cut, grandparent_of_cut, ..., root]`. - -3. **Walk backwards from cut to root, consuming budget**: - - Start at the cut element - - Walk backwards along the path to root - - For each value node encountered on the path: - - If value size ≤ remaining budget: render full value and deduct budget - - If value size > remaining budget: render type hint (``, ``, etc.) and **DO NOT** deduct budget (never let budget go below 0) - - If remaining budget < 50: set budget to 0 and enable "summary mode" - -4. **Summary mode (budget = 0)**: - - Continue walking towards root - - For elements on the **same level** as current position: render with type hints - - For elements on **higher levels** (closer to root): render only structure (keys/attributes) without values - just show the level hierarchy - -### Critical Constraints -- **Never let budget go below 0**: If a value is bigger than remaining budget, use type hint instead of rendering data -- **Budget allocation order**: Cut element → parent → grandparent → ... → root (bottom-up) -- **Rendering order**: Can be top-down (root → children) for structure, but budget must be allocated bottom-up -- **When budget < 50**: Set to 0 and enable summary mode immediately - -### Example Flow - -Given JSON structure (truncated at cut point): -```json -{ - "document": { - "metadata": { - "title": "My Document", - "author": "John Doe", - "version": 1 - }, - "sections": [ - { - "id": "section1", - "title": "Introduction", - "content": "This is the introduction content..." - }, - { - "id": "section2", - "title": "Main Content", - "content": "This is a very long content that gets cut right here in the middle of this sentence and the JSON is truncated..." -``` - -**Cut point**: The JSON is truncated in the middle of `sections[1].content` value. - -**Path from cut to root**: -``` -[ - sections[1].content (value - CUT ELEMENT), - sections[1].content (key-value pair), - sections[1] (object), - sections (array), - document (object), - root (object) -] -``` - -**Budget allocation order (walking backwards from cut → root)**: - -1. **`sections[1].content` (value)** - CUT ELEMENT - - Check: value size = 120 chars, budget = 500 - - Action: Render full value "This is a very long content that gets cut right here in the middle of this sentence and the JSON is truncated..." - - Deduct: 120 chars from budget → remaining = 380 - -2. **`sections[1]` (object)** - Parent of cut - - Action: Render structure only (no budget needed for `{`, `}`, keys) - - Render: `"id": "section2", "title": "Main Content", "content": ` - -3. **`sections` (array)** - Grandparent - - Action: Render structure only - - Render: `[, ]` where section2 already has content rendered - -4. **`document` (object)** - Great-grandparent - - Action: Render structure only - - Render: `"metadata": {...}, "sections": ` - -5. **`root` (object)** - Root - - Action: Render structure only - - Render: `{"document": }` - -**If budget becomes 0 during step 1** (e.g., value size = 600, budget = 500): -- `sections[1].content` gets type hint `` (value too big, don't deduct budget) -- Budget remains 500, but if < 50, set to 0 and enable summary mode -- Continue to root with summary mode: - - **Same level elements** (e.g., `sections[1].id`, `sections[1].title`): type hints (``) - - **Higher level elements** (e.g., `sections[0]`, `metadata`): structure only (keys, braces, no values) - -**Expected output with budget = 500** (sufficient budget): -```json -{ - "document": { - "metadata": { - "title": "My Document", - "author": "John Doe", - "version": 1 - }, - "sections": [ - { - "id": "section1", - "title": "Introduction", - "content": "This is the introduction content..." - }, - { - "id": "section2", - "title": "Main Content", - "content": "This is a very long content that gets cut right here in the middle of this sentence and the JSON is truncated..." -``` -*All values rendered because budget is sufficient.* - -**Expected output if ONE value is too big and budget running out** (budget = 100, cut value = 120): -```json -{ - "document": { - "metadata": >, - "sections": [ - { - "id": "section1", - "title": "Introduction", - "content": "This is the introduction content..." - }, - { - "id": "section2", - "title": , - "content": "This is a very long content that gets cut right here in the middle of this sentence and the JSON is truncated..." -``` - - - -**Key Points:** -- **Single value too big**: Only that value gets type hint, continue rendering other data -- **Budget > 0**: Render side paths (siblings, other branches) as long as budget allows -- **Budget = 0**: Stop rendering side paths, only render path from cut element to root (structure only for higher levels, type hints for same level) - -## Current Implementation Issues - -The current implementation in `jsonContinuation.py`: -- Pre-allocates budget to path elements before rendering -- But rendering still happens top-down, so root elements consume budget first -- Path elements check for pre-allocated budget, but non-path elements also consume budget during top-down rendering - -## Expected Behavior - -**Before budget runs out:** -- Cut element and path to root: full values rendered -- Other elements: full values if budget allows - -**After budget < 50 (summary mode):** -- Cut element and path to root: full values (if budget was allocated) -- Same level elements: type hints (``, ``, etc.) -- Higher level elements: structure only (keys, braces, brackets - no values) - -## Implementation Notes - -- The path should be built using `_findPathToRoot()` which walks from root to find cut element -- Budget should be consumed during a separate pass that walks the path (cut → root) -- During rendering, path elements should check if they have pre-allocated budget -- Non-path elements should only consume leftover budget after path elements are processed -- Structure elements (objects, arrays) don't consume budget - only values do diff --git a/modules/shared/_fixes.py b/modules/shared/_fixes.py deleted file mode 100644 index 35843533..00000000 --- a/modules/shared/_fixes.py +++ /dev/null @@ -1,305 +0,0 @@ -""" -FINAL CORRECTED _renderWithBudgetFromStructure - Version 3 - -This file contains the CORRECT implementation that should REPLACE -the existing methods in jsonContinuation.py - -KEY BEHAVIOR: -1. Budget is allocated from CUT → ROOT (not top-down) -2. Cut-near values get priority -3. When budget < 50: summary_mode enabled, non-path containers → / -4. Path containers always render their structure - -COPY THESE METHODS INTO YOUR JsonAnalyzer CLASS: -- _renderWithBudgetFromStructure (REPLACE existing) -- _buildPathFromCutToRootV3 (ADD) -- _collectAllValuesWithDistance (ADD) -- _renderNodeV3 (ADD) -- _renderObjectV3 (ADD) -- _renderArrayV3 (ADD) -- _renderValueV3 (ADD) -""" - -from typing import List, Set -from dataclasses import dataclass, field - - -@dataclass -class BudgetAllocation: - """Tracks which nodes have been allocated budget""" - allocated_node_ids: Set[int] = field(default_factory=set) - path_node_ids: Set[int] = field(default_factory=set) - summary_mode: bool = False - - -# ============================================================================= -# METHODS TO COPY INTO JsonAnalyzer CLASS -# ============================================================================= - -def _renderWithBudgetFromStructure(self, structure: dict, cutPos: int) -> str: - """ - Render structure with budget logic - allocate from CUT to ROOT. - - ALGORITHM: - - Phase 1: Build path from cut to root - - Find the cut element (truncated value or deepest incomplete node) - - Build ordered path: [cut_element, parent, grandparent, ..., root] - - Phase 2: Allocate budget - - Collect ALL value nodes with their distance to cut - - Sort by distance (smaller = closer to cut = higher priority) - - Allocate budget to values in this order - - When budget < 50: enable summary_mode (affects containers only) - - Phase 3: Render - - PATH containers: always render structure - - NON-PATH containers in summary_mode: render as / - - Values: render if allocated, else type hint - - Returns: - Rendered JSON string with budget constraints applied - """ - # Phase 1: Build path from cut to root - pathFromCutToRoot = [] - self._buildPathFromCutToRootV3(structure, cutPos, [], pathFromCutToRoot) - - pathNodeIds = set(id(node) for node in pathFromCutToRoot) - - # Phase 2: Collect ALL values and allocate budget - allValues = [] - self._collectAllValuesWithDistance(structure, cutPos, allValues) - - # Sort by distance (smaller = closer to cut = higher priority) - allValues.sort(key=lambda x: x['distance']) - - # Initialize allocation tracker - allocation = BudgetAllocation( - path_node_ids=pathNodeIds, - allocated_node_ids=set(), - summary_mode=False - ) - - remainingBudget = self.budgetLimit - - # Phase 2a: Allocate PATH values first (truncated values are always rendered) - pathValues = [item for item in allValues if id(item['node']) in pathNodeIds] - for item in pathValues: - node = item['node'] - nodeType = node.get('type') - - if nodeType == 'truncated_value': - allocation.allocated_node_ids.add(id(node)) - continue - - if nodeType != 'value': - continue - - rawValue = node.get('raw', '') - valueSize = len(rawValue) - - if valueSize <= remainingBudget: - allocation.allocated_node_ids.add(id(node)) - remainingBudget -= valueSize - - if remainingBudget < 50: - allocation.summary_mode = True - - # Phase 2b: Allocate NON-PATH values (skip if path already triggered summary mode) - if not allocation.summary_mode: - nonPathValues = [item for item in allValues if id(item['node']) not in pathNodeIds] - for item in nonPathValues: - node = item['node'] - nodeType = node.get('type') - - if nodeType != 'value': - continue - - rawValue = node.get('raw', '') - valueSize = len(rawValue) - - if valueSize <= remainingBudget: - allocation.allocated_node_ids.add(id(node)) - remainingBudget -= valueSize - - if remainingBudget < 50 and not allocation.summary_mode: - allocation.summary_mode = True - - # Phase 3: Render with allocation info - return self._renderNodeV3(structure, 0, allocation) - - -def _buildPathFromCutToRootV3(self, node: dict, cutPos: int, currentPath: list, resultPath: list) -> bool: - """ - Recursively find the path from root to cut element, then reverse it. - Result path is ordered: [cut_element, parent, ..., root] - """ - nodeType = node.get('type') - startPos = node.get('start_pos', 0) - endPos = node.get('end_pos', cutPos + 1) - - pathWithCurrent = currentPath + [node] - - for child in node.get('children', []): - if self._buildPathFromCutToRootV3(child, cutPos, pathWithCurrent, resultPath): - return True - - if nodeType == 'truncated_value': - resultPath.clear() - resultPath.extend(reversed(pathWithCurrent)) - return True - - if nodeType == 'value' and startPos <= cutPos <= endPos: - resultPath.clear() - resultPath.extend(reversed(pathWithCurrent)) - return True - - if nodeType in ('object', 'array') and not node.get('complete') and startPos <= cutPos: - resultPath.clear() - resultPath.extend(reversed(pathWithCurrent)) - return True - - if nodeType == 'root' and not resultPath: - resultPath.clear() - resultPath.extend(reversed(pathWithCurrent)) - return True - - return False - - -def _collectAllValuesWithDistance(self, node: dict, cutPos: int, result: list, depth: int = 0): - """Collect ALL value nodes with their distance to cut point.""" - nodeType = node.get('type') - - if nodeType in ('value', 'truncated_value'): - endPos = node.get('end_pos', cutPos) - distance = cutPos - endPos - result.append({ - 'node': node, - 'distance': distance, - 'depth': depth - }) - - for child in node.get('children', []): - self._collectAllValuesWithDistance(child, cutPos, result, depth + 1) - - -def _renderNodeV3(self, node: dict, depth: int, allocation) -> str: - """Render a node with budget allocation info.""" - nodeType = node.get('type') - - if nodeType == 'root': - parts = [] - for child in node.get('children', []): - parts.append(self._renderNodeV3(child, depth, allocation)) - return '\n'.join(parts) - - elif nodeType == 'object': - return self._renderObjectV3(node, depth, allocation) - - elif nodeType == 'array': - return self._renderArrayV3(node, depth, allocation) - - elif nodeType == 'value': - return self._renderValueV3(node, depth, allocation) - - elif nodeType == 'truncated_value': - keyPrefix = f'"{node.get("key")}": ' if node.get('key') else '' - return f"{keyPrefix}{node.get('raw', '')}" - - return '' - - -def _renderObjectV3(self, node: dict, depth: int, allocation) -> str: - """Render object - summary mode non-path objects become .""" - indentStr = " " * depth - innerIndent = " " * (depth + 1) - - keyPrefix = f'"{node.get("key")}": ' if node.get('key') else '' - children = node.get('children', []) - isOnPath = id(node) in allocation.path_node_ids - - if allocation.summary_mode and not isOnPath: - return f"{keyPrefix}" - - if not children: - return f"{keyPrefix}{{}}" if node.get('complete') else f"{keyPrefix}{{" - - parts = [f"{keyPrefix}{{"] - - for i, child in enumerate(children): - childRendered = self._renderNodeV3(child, depth + 1, allocation) - isLast = (i == len(children) - 1) - isTruncated = child.get('type') == 'truncated_value' - - if isLast or isTruncated: - parts.append(f"{innerIndent}{childRendered}") - else: - parts.append(f"{innerIndent}{childRendered},") - - if node.get('complete'): - parts.append(f"{indentStr}}}") - - return '\n'.join(parts) - - -def _renderArrayV3(self, node: dict, depth: int, allocation) -> str: - """Render array - summary mode non-path arrays become .""" - indentStr = " " * depth - innerIndent = " " * (depth + 1) - - keyPrefix = f'"{node.get("key")}": ' if node.get('key') else '' - children = node.get('children', []) - isOnPath = id(node) in allocation.path_node_ids - - if allocation.summary_mode and not isOnPath: - return f"{keyPrefix}" - - if not children: - return f"{keyPrefix}[]" if node.get('complete') else f"{keyPrefix}[" - - parts = [f"{keyPrefix}["] - - for i, child in enumerate(children): - childRendered = self._renderNodeV3(child, depth + 1, allocation) - isLast = (i == len(children) - 1) - isTruncated = child.get('type') == 'truncated_value' - - if isLast or isTruncated: - parts.append(f"{innerIndent}{childRendered}") - else: - parts.append(f"{innerIndent}{childRendered},") - - if node.get('complete'): - parts.append(f"{indentStr}]") - - return '\n'.join(parts) - - -def _renderValueV3(self, node: dict, depth: int, allocation) -> str: - """Render value - if allocated render full, else type hint.""" - keyPrefix = f'"{node.get("key")}": ' if node.get('key') else '' - rawValue = node.get('raw', '""') - valueType = node.get('value_type', 'string') - - typeHints = { - 'string': '', - 'number': '', - 'boolean': '', - 'null': '' - } - typeHint = typeHints.get(valueType, '') - - if id(node) in allocation.allocated_node_ids: - return f"{keyPrefix}{rawValue}" - else: - return f"{keyPrefix}{typeHint}" - - -# ============================================================================= -# ALSO ADD THIS IMPORT AT THE TOP OF YOUR FILE -# ============================================================================= -# from dataclasses import dataclass, field -# from typing import Set - -# And add the BudgetAllocation class inside your file or as a nested class \ No newline at end of file diff --git a/modules/shared/_safety_copy_jsonContinuation.py b/modules/shared/_safety_copy_jsonContinuation.py deleted file mode 100644 index 38ce0045..00000000 --- a/modules/shared/_safety_copy_jsonContinuation.py +++ /dev/null @@ -1,1410 +0,0 @@ -""" -JSON Continuation Context Module - -Generiert drei Kontexte für abgeschnittene JSON-Strings: -1. Overlap Context: Das innerste Objekt/Array-Element, das den Cut-Punkt enthält -2. Hierarchy Context: Die hierarchische Struktur vom Root bis zum Cut mit Budget-Logik -3. Complete Part: Der vollständige Teil des JSONs mit allen Strukturen geschlossen - -Hauptfunktionen: -- extractContinuationContexts(truncatedJson: str) -> Tuple[str, str, str] - Extrahiert alle drei Kontexte aus einem abgeschnittenen JSON-String. - -- getContexts(truncatedJson: str) -> JsonContinuationContexts - Gibt alle Kontexte als Pydantic-Modell zurück mit benannten Feldern. - -Modulkonstanten: -- BUDGET_LIMIT: int = 500 - Zeichen-Budget für vollständige Datenwerte im Hierarchy Context - -- OVERLAP_MAX_CHARS: int = 1000 - Maximale Zeichen für den Overlap Context - -Verwendung: - >>> from modules.shared.jsonContinuation import getContexts - >>> jsonStr = '{"users": [{"name": "John", "bio": "Hello Wor' - >>> contexts = getContexts(jsonStr) - >>> print(contexts.overlapContext) - >>> print(contexts.hierarchyContext) - >>> print(contexts.completePart) - -Autor: Claude -Version: 2.0 -""" - -from typing import Tuple, List, Optional, Any -from dataclasses import dataclass -from enum import Enum -from modules.datamodels.datamodelAi import JsonContinuationContexts - - -# ============================================================================= -# MODULE CONSTANTS -# ============================================================================= - -BUDGET_LIMIT: int = 500 -"""Zeichen-Budget für vollständige Datenwerte im Hierarchy Context""" - -OVERLAP_MAX_CHARS: int = 1000 -"""Maximale Zeichen für den Overlap Context""" - - -# ============================================================================= -# TOKEN TYPES AND DATA CLASSES -# ============================================================================= - - -class TokenType(Enum): - """JSON Token Types""" - OBJECT_START = "{" - OBJECT_END = "}" - ARRAY_START = "[" - ARRAY_END = "]" - STRING = "string" - NUMBER = "number" - BOOLEAN = "boolean" - NULL = "null" - COLON = ":" - COMMA = "," - KEY = "key" - EOF = "eof" - TRUNCATED = "truncated" - - -@dataclass -class Token: - """Represents a JSON token with position info""" - type: TokenType - value: Any - start_pos: int - end_pos: int - raw: str # Original string representation - - -@dataclass -class StackFrame: - """Represents a level in the JSON hierarchy""" - type: str # "object" or "array" - start_pos: int - key: Optional[str] = None # Current key for objects - index: int = 0 # Current index for arrays - content: str = "" # Accumulated content for this frame - keys_seen: List[str] = None # Keys seen in this object - - def __post_init__(self): - if self.keys_seen is None: - self.keys_seen = [] - - -class JsonTokenizer: - """Tokenizer for potentially truncated JSON strings""" - - def __init__(self, jsonStr: str): - self.jsonStr = jsonStr - self.pos = 0 - self.length = len(jsonStr) - - def skipWhitespace(self): - """Skip whitespace characters""" - while self.pos < self.length and self.jsonStr[self.pos] in ' \t\n\r': - self.pos += 1 - - def peek(self) -> Optional[str]: - """Peek at current character without consuming""" - if self.pos < self.length: - return self.jsonStr[self.pos] - return None - - def readString(self) -> Token: - """Read a JSON string token""" - start_pos = self.pos - self.pos += 1 # Skip opening quote - - escaped = False - while self.pos < self.length: - char = self.jsonStr[self.pos] - if escaped: - escaped = False - self.pos += 1 - elif char == '\\': - escaped = True - self.pos += 1 - elif char == '"': - self.pos += 1 - raw = self.jsonStr[start_pos:self.pos] - try: - # Try to decode the string value - value = raw[1:-1] # Remove quotes for value - except: - value = raw - return Token(TokenType.STRING, value, start_pos, self.pos, raw) - else: - self.pos += 1 - - # String was truncated - raw = self.jsonStr[start_pos:self.pos] - return Token(TokenType.TRUNCATED, raw[1:] if len(raw) > 1 else "", start_pos, self.pos, raw) - - def readNumber(self) -> Token: - """Read a JSON number token""" - start_pos = self.pos - - # Handle negative - if self.pos < self.length and self.jsonStr[self.pos] == '-': - self.pos += 1 - - # Read digits - while self.pos < self.length and self.jsonStr[self.pos].isdigit(): - self.pos += 1 - - # Decimal part - if self.pos < self.length and self.jsonStr[self.pos] == '.': - self.pos += 1 - while self.pos < self.length and self.jsonStr[self.pos].isdigit(): - self.pos += 1 - - # Exponent - if self.pos < self.length and self.jsonStr[self.pos] in 'eE': - self.pos += 1 - if self.pos < self.length and self.jsonStr[self.pos] in '+-': - self.pos += 1 - while self.pos < self.length and self.jsonStr[self.pos].isdigit(): - self.pos += 1 - - raw = self.jsonStr[start_pos:self.pos] - try: - value = float(raw) if '.' in raw or 'e' in raw.lower() else int(raw) - except ValueError: - value = raw - - return Token(TokenType.NUMBER, value, start_pos, self.pos, raw) - - def readKeyword(self) -> Token: - """Read true, false, or null""" - start_pos = self.pos - - for keyword, token_type in [('true', TokenType.BOOLEAN), - ('false', TokenType.BOOLEAN), - ('null', TokenType.NULL)]: - if self.jsonStr[self.pos:].startswith(keyword): - self.pos += len(keyword) - value = True if keyword == 'true' else (False if keyword == 'false' else None) - return Token(token_type, value, start_pos, self.pos, keyword) - - # Partial keyword (truncated) - while self.pos < self.length and self.jsonStr[self.pos].isalpha(): - self.pos += 1 - raw = self.jsonStr[start_pos:self.pos] - return Token(TokenType.TRUNCATED, raw, start_pos, self.pos, raw) - - def nextToken(self) -> Token: - """Get the next token""" - self.skipWhitespace() - - if self.pos >= self.length: - return Token(TokenType.EOF, None, self.pos, self.pos, "") - - char = self.jsonStr[self.pos] - startPos = self.pos - - if char == '{': - self.pos += 1 - return Token(TokenType.OBJECT_START, '{', startPos, self.pos, '{') - elif char == '}': - self.pos += 1 - return Token(TokenType.OBJECT_END, '}', startPos, self.pos, '}') - elif char == '[': - self.pos += 1 - return Token(TokenType.ARRAY_START, '[', startPos, self.pos, '[') - elif char == ']': - self.pos += 1 - return Token(TokenType.ARRAY_END, ']', startPos, self.pos, ']') - elif char == ':': - self.pos += 1 - return Token(TokenType.COLON, ':', startPos, self.pos, ':') - elif char == ',': - self.pos += 1 - return Token(TokenType.COMMA, ',', startPos, self.pos, ',') - elif char == '"': - return self.readString() - elif char == '-' or char.isdigit(): - return self.readNumber() - elif char.isalpha(): - return self.readKeyword() - else: - # Unknown character, treat as truncated - self.pos += 1 - return Token(TokenType.TRUNCATED, char, startPos, self.pos, char) - - -@dataclass -class HierarchyLevel: - """Represents one level in the parsed hierarchy""" - type: str # "object" or "array" - start_pos: int - end_pos: int # -1 if not closed - key: Optional[str] # Key if this is a value in an object - index: Optional[int] # Index if this is in an array - content: dict # Parsed content at this level - raw_start: str # Raw string from start to children - children_content: List[Any] # For arrays: list of parsed elements - - -def getJsonContinuationContext( - truncatedJson: str, - budgetLimit: Optional[int] = None, - overlapMaxChars: Optional[int] = None -) -> Tuple[str, str, str]: - """ - Generate continuation contexts for a truncated JSON string. - - Generiert drei Kontexte für abgeschnittene JSON-Strings: - 1. Overlap Context: Das innerste Objekt/Array-Element, das den Cut-Punkt enthält - 2. Hierarchy Context: Die hierarchische Struktur vom Root bis zum Cut mit Budget-Logik - 3. Complete Part: Der vollständige Teil des JSONs mit allen Strukturen geschlossen - - Args: - truncatedJson: The truncated JSON string - budgetLimit: Character budget for data values in hierarchy context (uses BUDGET_LIMIT if None) - overlapMaxChars: Maximum characters for overlap context (uses OVERLAP_MAX_CHARS if None) - - Returns: - Tuple of (overlapContext, hierarchyContext, completePart): - - overlapContext: The innermost object/element containing the cut (for merging) - - hierarchyContext: Full structure from root to cut with budget-limited values - - completePart: Valid JSON with all structures properly closed - """ - if budgetLimit is None: - budgetLimit = BUDGET_LIMIT - if overlapMaxChars is None: - overlapMaxChars = OVERLAP_MAX_CHARS - - analyzer = JsonAnalyzer(truncatedJson, budgetLimit, overlapMaxChars) - return analyzer.analyze() - - -class JsonAnalyzer: - """ - Analyzes truncated JSON and generates continuation contexts. - - Generates three contexts for truncated JSON strings: - 1. Overlap Context: The innermost object/array element containing the cut point - 2. Hierarchy Context: The hierarchical structure from root to cut with budget logic - 3. Complete Part: The complete part of the JSON with all structures properly closed - """ - - def __init__(self, jsonStr: str, budgetLimit: Optional[int] = None, overlapMaxChars: Optional[int] = None): - self.jsonStr = jsonStr - self.budgetLimit = budgetLimit if budgetLimit is not None else BUDGET_LIMIT - self.overlapMaxChars = overlapMaxChars if overlapMaxChars is not None else OVERLAP_MAX_CHARS - self.stack: List[StackFrame] = [] - self.hierarchy: List[dict] = [] # Parsed hierarchy info - - def analyze(self) -> Tuple[str, str, str]: - """ - Analyze the truncated JSON and return all three contexts. - - Returns: - Tuple of (overlapContext, hierarchyContext, completePart) - """ - # Parse and track the structure - self._parseStructure() - - # Generate overlap context - overlapContext = self._generateOverlapContext() - - # Generate hierarchy context (use improved version) - hierarchyContext = self._renderWithBudgetV2() - - # Generate complete part (JSON with all structures closed) - completePart = self._generateCompletePart() - - return overlapContext, hierarchyContext, completePart - - def _generateCompletePart(self) -> str: - """ - Generate the complete part of the JSON with all structures properly closed. - - This creates valid JSON by closing all open strings, brackets/braces. - Unvollständige Keys werden entfernt, damit das Ergebnis valides JSON ist. - Unvollständige Keywords (true, false, null) werden vervollständigt. - - Strategy: - 1. Take the full truncated JSON - 2. If we're in the middle of a string, close it - 3. Complete incomplete keywords (tr → true, f → false, n → null) - 4. Remove incomplete key-value pairs (keys without values) - 5. Close all open brackets/braces - """ - result = self.jsonStr.rstrip() - - # Remove trailing comma if present (after stripping) - if result.endswith(','): - result = result[:-1] - - # Check if we need to close an open string - stringClosing = self._getStringClosing(result) - result += stringClosing - - # Complete incomplete keywords (true, false, null) - result = self._completeIncompleteKeywords(result) - - # Check if we're in the middle of a key (after colon) - # If string was just closed and we're after a colon with no value, remove the key - result = self._cleanIncompleteKeyValue(result) - - # Close all open structures - closingBrackets = self._getClosingBrackets(result) - - return result + closingBrackets - - def _getStringClosing(self, jsonStr: str) -> str: - """Check if there's an unclosed string and return closing quote if needed.""" - in_string = False - escaped = False - - for char in jsonStr: - if escaped: - escaped = False - continue - - if char == '\\' and in_string: - escaped = True - continue - - if char == '"': - in_string = not in_string - - return '"' if in_string else "" - - def _cleanIncompleteKeyValue(self, jsonStr: str) -> str: - """ - Clean up incomplete key-value pairs. - Handles cases like: - - {"key": "incompl -> keep (valid truncated value) - - {"key": -> remove key - - {"a": 1, "key -> remove incomplete key (was in middle of key name) - """ - stripped = jsonStr.rstrip() - - # Pattern: ends with colon (possibly with whitespace) - incomplete value - if stripped.endswith(':'): - # Find the start of this key and remove the whole key-value - return self._removeLastKey(stripped) - - # Check if we just closed a string that was an incomplete key - # Pattern: ..., "something" or { "something" where something has no colon after - # This happens when we close a truncated key name like "add" -> "add" - if stripped.endswith('"'): - # Look for the pattern: comma/bracket + whitespace + "string" - # and check if this was supposed to be a key - if self._isIncompleteKey(stripped): - return self._removeLastKey(stripped) - - return jsonStr - - def _completeIncompleteKeywords(self, jsonStr: str) -> str: - """ - Complete incomplete JSON keywords at the end of the string. - - Checks the last element for incomplete keywords after colon: - - ": t*" or ": f*" or ": n*" -> complete to true/false/null - - ": " or ":" (without keyword) -> set to null - """ - result = jsonStr.rstrip() - - # Find the last colon (not in string) - in_string = False - escaped = False - last_colon_pos = -1 - - for i in range(len(result) - 1, -1, -1): - char = result[i] - - if escaped: - escaped = False - continue - - if char == '\\' and in_string: - escaped = True - continue - - if char == '"': - in_string = not in_string - continue - - if not in_string and char == ':': - last_colon_pos = i - break - - if last_colon_pos < 0: - return result - - # Get text after the last colon - after_colon = result[last_colon_pos + 1:].strip() - - # Check for incomplete keyword patterns - if after_colon.startswith('t') or after_colon.startswith('T'): - # Incomplete true - keyword_start = last_colon_pos + 1 - # Skip whitespace - while keyword_start < len(result) and result[keyword_start] in ' \t\n\r': - keyword_start += 1 - # Remove partial keyword - keyword_end = keyword_start + 1 - while keyword_end < len(result) and result[keyword_end].isalpha(): - keyword_end += 1 - return result[:keyword_start] + 'true' + result[keyword_end:] - - elif after_colon.startswith('f') or after_colon.startswith('F'): - # Incomplete false - keyword_start = last_colon_pos + 1 - while keyword_start < len(result) and result[keyword_start] in ' \t\n\r': - keyword_start += 1 - keyword_end = keyword_start + 1 - while keyword_end < len(result) and result[keyword_end].isalpha(): - keyword_end += 1 - return result[:keyword_start] + 'false' + result[keyword_end:] - - elif after_colon.startswith('n') or after_colon.startswith('N'): - # Incomplete null - keyword_start = last_colon_pos + 1 - while keyword_start < len(result) and result[keyword_start] in ' \t\n\r': - keyword_start += 1 - keyword_end = keyword_start + 1 - while keyword_end < len(result) and result[keyword_end].isalpha(): - keyword_end += 1 - return result[:keyword_start] + 'null' + result[keyword_end:] - - elif not after_colon or after_colon == '': - # No keyword after colon -> set to null - return result + 'null' - - return result - - def _isIncompleteKey(self, jsonStr: str) -> bool: - """ - Check if the last string in the JSON is an incomplete key in an object. - This happens when truncation occurred in the middle of a key name. - Only applies to objects, not arrays. - """ - # Find the last complete string - pos = len(jsonStr) - 1 - if jsonStr[pos] != '"': - return False - - # Find the opening quote of this string - stringStart = pos - 1 - while stringStart >= 0: - if jsonStr[stringStart] == '"': - # Check it's not escaped - numBackslashes = 0 - checkPos = stringStart - 1 - while checkPos >= 0 and jsonStr[checkPos] == '\\': - numBackslashes += 1 - checkPos -= 1 - if numBackslashes % 2 == 0: - break - stringStart -= 1 - - if stringStart < 0: - return False - - # Now stringStart points to opening quote - # Check what's before it (skip whitespace) - beforePos = stringStart - 1 - while beforePos >= 0 and jsonStr[beforePos] in ' \t\n\r': - beforePos -= 1 - - if beforePos < 0: - return False - - # For this to be an incomplete key, it must be preceded by { or , - # AND we must be inside an object (not an array) - if jsonStr[beforePos] not in ',{': - return False - - # Now check if we're in an object context (not array) - # Count open braces/brackets to determine context - braceCount = 0 - bracketCount = 0 - inString = False - - for i in range(beforePos + 1): - char = jsonStr[i] - if char == '"' and (i == 0 or jsonStr[i-1] != '\\'): - inString = not inString - elif not inString: - if char == '{': - braceCount += 1 - elif char == '}': - braceCount -= 1 - elif char == '[': - bracketCount += 1 - elif char == ']': - bracketCount -= 1 - - # If we have more open braces than brackets at this point, - # we're in an object context - # Actually, we need to check the innermost container - # Let's track the stack properly - stack = [] - inString = False - - for i in range(beforePos + 1): - char = jsonStr[i] - if char == '"' and (i == 0 or jsonStr[i-1] != '\\'): - inString = not inString - elif not inString: - if char == '{': - stack.append('object') - elif char == '[': - stack.append('array') - elif char == '}': - if stack and stack[-1] == 'object': - stack.pop() - elif char == ']': - if stack and stack[-1] == 'array': - stack.pop() - - # If innermost container is an object, this is an incomplete key - return len(stack) > 0 and stack[-1] == 'object' - - def _removeLastKey(self, jsonStr: str) -> str: - """Remove the last incomplete key-value pair from the JSON string.""" - stripped = jsonStr.rstrip() - - # Find the last comma or opening bracket before the incomplete key - pos = len(stripped) - 1 - - # Skip past the current string/key - in_string = False - while pos >= 0: - char = stripped[pos] - if char == '"' and (pos == 0 or stripped[pos-1] != '\\'): - in_string = not in_string - if not in_string and char in ',{': - break - pos -= 1 - - if pos < 0: - return stripped - - if stripped[pos] == ',': - # Remove from comma onwards - return stripped[:pos] - elif stripped[pos] == '{': - # Keep the opening brace - return stripped[:pos+1] - - return stripped - - def _findLastCompletePosition(self) -> int: - """Find the position of the last complete value in the JSON.""" - tokenizer = JsonTokenizer(self.jsonStr) - last_complete_pos = 0 - stack_depth = 0 - last_value_end = 0 - in_value = False - - while True: - token = tokenizer.nextToken() - - if token.type == TokenType.EOF: - break - - if token.type == TokenType.TRUNCATED: - # Return position before the truncated part - break - - if token.type in (TokenType.OBJECT_START, TokenType.ARRAY_START): - stack_depth += 1 - in_value = True - - elif token.type in (TokenType.OBJECT_END, TokenType.ARRAY_END): - stack_depth -= 1 - last_value_end = token.end_pos - in_value = False - - elif token.type == TokenType.STRING: - # Check if this is a key or a value - saved_pos = tokenizer.pos - tokenizer.skipWhitespace() - next_char = tokenizer.peek() - tokenizer.pos = saved_pos - - if next_char != ':': - # It's a value - last_value_end = token.end_pos - in_value = False - - elif token.type in (TokenType.NUMBER, TokenType.BOOLEAN, TokenType.NULL): - last_value_end = token.end_pos - in_value = False - - elif token.type == TokenType.COMMA: - # After a comma, we've completed a value - last_complete_pos = last_value_end - - # Return the last complete position - return last_value_end if last_value_end > 0 else len(self.jsonStr) - - def _getClosingBrackets(self, jsonStr: str) -> str: - """Determine what closing brackets are needed.""" - stack = [] - in_string = False - escaped = False - - for char in jsonStr: - if escaped: - escaped = False - continue - - if char == '\\' and in_string: - escaped = True - continue - - if char == '"': - in_string = not in_string - continue - - if in_string: - continue - - if char == '{': - stack.append('}') - elif char == '[': - stack.append(']') - elif char == '}': - if stack and stack[-1] == '}': - stack.pop() - elif char == ']': - if stack and stack[-1] == ']': - stack.pop() - - # Return closing brackets in reverse order - return ''.join(reversed(stack)) - - def _parseStructure(self): - """Parse the JSON structure and track hierarchy""" - tokenizer = JsonTokenizer(self.jsonStr) - - while True: - token = tokenizer.nextToken() - - if token.type == TokenType.EOF or token.type == TokenType.TRUNCATED: - break - - if token.type == TokenType.OBJECT_START: - frame = StackFrame( - type="object", - start_pos=token.start_pos, - keys_seen=[] - ) - self.stack.append(frame) - - elif token.type == TokenType.ARRAY_START: - frame = StackFrame( - type="array", - start_pos=token.start_pos, - index=0 - ) - self.stack.append(frame) - - elif token.type == TokenType.OBJECT_END: - if self.stack and self.stack[-1].type == "object": - self.stack.pop() - - elif token.type == TokenType.ARRAY_END: - if self.stack and self.stack[-1].type == "array": - self.stack.pop() - - elif token.type == TokenType.STRING: - # Could be a key or a value - self._handleStringToken(token, tokenizer) - - elif token.type == TokenType.COMMA: - # Increment array index - if self.stack and self.stack[-1].type == "array": - self.stack[-1].index += 1 - - def _handleStringToken(self, token: Token, tokenizer: JsonTokenizer): - """Handle a string token (could be key or value)""" - if self.stack and self.stack[-1].type == "object": - # Check if this is a key (followed by colon) - saved_pos = tokenizer.pos - tokenizer.skipWhitespace() - next_char = tokenizer.peek() - - if next_char == ':': - # This is a key - self.stack[-1].key = token.value - self.stack[-1].keys_seen.append(token.value) - - tokenizer.pos = saved_pos - - def _generateOverlapContext(self) -> str: - """ - Generate the overlap context - the innermost object/array element containing the cut. - - Returns the raw string from the start of that element to the end of the truncated JSON. - Dieser Kontext wird verwendet, um den abgeschnittenen Teil mit dem neuen Teil zu mergen. - Exakt so wie im Original-String (für String-Matching beim Merge). - - SPECIAL CASE: If cut point is within a list item, return only: - - The broken list item (containing the cut) - - The list item before it (if available) - This avoids returning the entire list when only a single item is broken. - """ - if not self.stack: - # No structure, return last overlap_max_chars characters - return self.jsonStr[-self.overlapMaxChars:] - - # Find the innermost container that should be the overlap - innermost = self.stack[-1] - - # SPECIAL CASE: If innermost is an array and cut is within a list item, - # return only the broken item and the previous item (if available) - if innermost.type == "array": - overlap_start = self._findOverlapStartForArray(innermost) - else: - # For objects, use the standard logic - overlap_start = self._findInnermostElementStart() - - overlap = self.jsonStr[overlap_start:] - - # Apply max chars limit - if len(overlap) > self.overlapMaxChars: - overlap = self.jsonStr[-self.overlapMaxChars:] - - return overlap - - def _findOverlapStartForArray(self, arrayFrame: StackFrame) -> int: - """ - Find overlap start for array: return only the broken list item and previous item. - - If cut point is within a list item, returns start of previous item (if available), - otherwise returns start of current (broken) item. - """ - # Find all element start positions in the array - element_starts = self._findAllArrayElementStarts(arrayFrame) - - if not element_starts: - # No elements found, fall back to array start - return arrayFrame.start_pos - - current_index = arrayFrame.index - - # If we're at index 0, there's no previous item - return current item start - if current_index == 0: - return element_starts[0] - - # If current_index is beyond known elements, use last known element - if current_index >= len(element_starts): - # Return start of second-to-last element (previous to last) - if len(element_starts) >= 2: - return element_starts[-2] # Previous item - else: - return element_starts[0] # Only one item, return it - - # Return start of previous item (current_index - 1) - return element_starts[current_index - 1] - - def _findAllArrayElementStarts(self, arrayFrame: StackFrame) -> List[int]: - """Find all element start positions in an array""" - arrayContent = self.jsonStr[arrayFrame.start_pos:] - - # Skip the opening bracket and whitespace - pos = 1 - while pos < len(arrayContent) and arrayContent[pos] in ' \t\n\r': - pos += 1 - - elementStarts = [arrayFrame.start_pos + pos] - depth = 0 - inString = False - escaped = False - - i = pos - while i < len(arrayContent): - char = arrayContent[i] - - if escaped: - escaped = False - i += 1 - continue - - if char == '\\' and inString: - escaped = True - i += 1 - continue - - if char == '"': - inString = not inString - i += 1 - continue - - if inString: - i += 1 - continue - - if char in '{[': - depth += 1 - elif char in '}]': - depth -= 1 - elif char == ',' and depth == 0: - # Found element boundary - i += 1 - # Skip whitespace - while i < len(arrayContent) and arrayContent[i] in ' \t\n\r': - i += 1 - elementStarts.append(arrayFrame.start_pos + i) - - i += 1 - - return elementStarts - - def _findInnermostElementStart(self) -> int: - """Find the start position of the innermost element for overlap""" - if not self.stack: - return max(0, len(self.jsonStr) - self.overlapMaxChars) - - # Walk through stack to find the innermost array element or object - # We want the innermost "atomic" unit that contains the cut - - # Strategy: - # - If innermost is an object: return its start - # - If innermost is an array: - # - If current element is an object/array: return start of that element - # - If current element is a primitive: return start of array or last N chars - - innermost = self.stack[-1] - - if innermost.type == "object": - return innermost.start_pos - else: - # It's an array - find the start of the current element - element_start = self._findArrayElementStart(innermost) - - # Check if the element is a primitive or complex type - element_content = self.jsonStr[element_start:].strip() - - # If it starts with { or [ it's complex, return the element start - if element_content and element_content[0] in '{[': - return element_start - else: - # Primitive in array - check if there's a parent object - # or return overlap_max_chars from end - for i in range(len(self.stack) - 2, -1, -1): - if self.stack[i].type == "object": - return self.stack[i].start_pos - - # No parent object, return max chars from end - return max(0, len(self.jsonStr) - self.overlapMaxChars) - - def _findArrayElementStart(self, arrayFrame: StackFrame) -> int: - """Find the start position of the current array element""" - # We need to find the start of the current element in the array - # Parse from array start to find element boundaries - - arrayContent = self.jsonStr[arrayFrame.start_pos:] - - # Skip the opening bracket and whitespace - pos = 1 - while pos < len(arrayContent) and arrayContent[pos] in ' \t\n\r': - pos += 1 - - elementStarts = [arrayFrame.start_pos + pos] - depth = 0 - inString = False - escaped = False - - i = pos - while i < len(arrayContent): - char = arrayContent[i] - - if escaped: - escaped = False - i += 1 - continue - - if char == '\\' and inString: - escaped = True - i += 1 - continue - - if char == '"': - inString = not inString - i += 1 - continue - - if inString: - i += 1 - continue - - if char in '{[': - depth += 1 - elif char in '}]': - depth -= 1 - elif char == ',' and depth == 0: - # Found element boundary - i += 1 - # Skip whitespace - while i < len(arrayContent) and arrayContent[i] in ' \t\n\r': - i += 1 - elementStarts.append(arrayFrame.start_pos + i) - - i += 1 - - # Return the start of the current element - if arrayFrame.index < len(elementStarts): - return elementStarts[arrayFrame.index] - elif elementStarts: - return elementStarts[-1] - else: - return arrayFrame.start_pos - - def _generateHierarchyContext(self) -> str: - """ - Generate the hierarchy context with budget logic. - Shows structure from root to cut point with data values limited by budget. - """ - if not self.stack: - # No structure - return self.jsonStr[-self.overlapMaxChars:] - - # We need to rebuild the JSON with budget logic - # Priority: elements closer to cut get full values, distant ones get "..." - - return self._rebuildWithBudget() - - def _rebuildWithBudget(self) -> str: - """Rebuild JSON from root to cut with budget constraints""" - - # Strategy: - # 1. Parse the JSON structure tracking all values - # 2. Calculate total value size - # 3. Apply budget from cut backwards - # 4. Render with "..." for values outside budget - - # First, get a structured representation - structure = self._parseForHierarchy() - - # Now render with budget - return self._renderWithBudget(structure) - - def _parseForHierarchy(self) -> dict: - """Parse JSON into a structure suitable for hierarchy rendering""" - - result = { - 'type': 'root', - 'children': [], - 'raw_positions': [] - } - - tokenizer = JsonTokenizer(self.jsonStr) - stack = [result] - current_key = None - - while True: - token = tokenizer.nextToken() - - if token.type == TokenType.EOF: - break - - if token.type == TokenType.TRUNCATED: - # Mark the truncation point - if stack: - current = stack[-1] - if current.get('type') == 'object': - if current_key: - current['children'].append({ - 'type': 'truncated_value', - 'key': current_key, - 'raw': self.jsonStr[token.start_pos:], - 'start_pos': token.start_pos - }) - elif current.get('type') == 'array': - current['children'].append({ - 'type': 'truncated_value', - 'raw': self.jsonStr[token.start_pos:], - 'start_pos': token.start_pos - }) - break - - if token.type == TokenType.OBJECT_START: - obj = { - 'type': 'object', - 'key': current_key, - 'children': [], - 'start_pos': token.start_pos - } - if stack: - stack[-1]['children'].append(obj) - stack.append(obj) - current_key = None - - elif token.type == TokenType.ARRAY_START: - arr = { - 'type': 'array', - 'key': current_key, - 'children': [], - 'start_pos': token.start_pos - } - if stack: - stack[-1]['children'].append(arr) - stack.append(arr) - current_key = None - - elif token.type == TokenType.OBJECT_END: - if len(stack) > 1 and stack[-1].get('type') == 'object': - stack[-1]['end_pos'] = token.end_pos - stack[-1]['complete'] = True - stack.pop() - - elif token.type == TokenType.ARRAY_END: - if len(stack) > 1 and stack[-1].get('type') == 'array': - stack[-1]['end_pos'] = token.end_pos - stack[-1]['complete'] = True - stack.pop() - - elif token.type == TokenType.STRING: - # Check if it's a key - saved_pos = tokenizer.pos - tokenizer.skipWhitespace() - next_char = tokenizer.peek() - - if next_char == ':' and stack and stack[-1].get('type') == 'object': - current_key = token.value - else: - # It's a value - value_node = { - 'type': 'value', - 'key': current_key, - 'value': token.value, - 'raw': token.raw, - 'start_pos': token.start_pos, - 'end_pos': token.end_pos, - 'value_type': 'string' - } - if stack: - stack[-1]['children'].append(value_node) - current_key = None - - tokenizer.pos = saved_pos - - elif token.type in (TokenType.NUMBER, TokenType.BOOLEAN, TokenType.NULL): - value_node = { - 'type': 'value', - 'key': current_key, - 'value': token.value, - 'raw': token.raw, - 'start_pos': token.start_pos, - 'end_pos': token.end_pos, - 'value_type': str(token.type.value) - } - if stack: - stack[-1]['children'].append(value_node) - current_key = None - - return result - - def _renderWithBudget(self, structure: dict) -> str: - """Render the structure with budget constraints""" - - # First, collect all value nodes with their distances from cut - cutPos = len(self.jsonStr) - allValues = self._collectValuesWithDistance(structure, cutPos) - - # Sort by distance (closest to cut first) - allValues.sort(key=lambda x: x['distance']) - - # Determine which values get full rendering - budgetRemaining = self.budgetLimit - valuesWithBudget = set() - - for valInfo in allValues: - valSize = len(str(valInfo['raw'])) - if budgetRemaining >= valSize: - valuesWithBudget.add(valInfo['id']) - budgetRemaining -= valSize - - # Now render the structure - return self._renderNode(structure, valuesWithBudget, indent=0) - - def _collectValuesWithDistance(self, node: dict, cutPos: int, depth: int = 0) -> list: - """Collect all value nodes with their distance from cut point""" - values = [] - - if node.get('type') == 'value': - endPos = node.get('end_pos', cutPos) - distance = cutPos - endPos - values.append({ - 'id': id(node), - 'node': node, - 'distance': distance, - 'raw': node.get('raw', ''), - 'depth': depth - }) - elif node.get('type') == 'truncated_value': - values.append({ - 'id': id(node), - 'node': node, - 'distance': 0, # Truncated values are at the cut - 'raw': node.get('raw', ''), - 'depth': depth - }) - - for child in node.get('children', []): - values.extend(self._collectValuesWithDistance(child, cutPos, depth + 1)) - - return values - - def _renderNode(self, node: dict, valuesWithBudget: set, indent: int = 0) -> str: - """Render a node with budget constraints""" - indent_str = " " * indent - - node_type = node.get('type') - - if node_type == 'root': - parts = [] - for child in node.get('children', []): - parts.append(self._renderNode(child, valuesWithBudget, indent)) - return '\n'.join(parts) - - elif node_type == 'object': - return self._renderObject(node, valuesWithBudget, indent) - - elif node_type == 'array': - return self._renderArray(node, valuesWithBudget, indent) - - elif node_type == 'value': - return self._renderValue(node, valuesWithBudget, indent) - - elif node_type == 'truncated_value': - return node.get('raw', '') - - return '' - - def _renderObject(self, node: dict, valuesWithBudget: set, indent: int) -> str: - """Render an object node""" - indent_str = " " * indent - inner_indent = " " * (indent + 1) - - key_prefix = "" - if node.get('key'): - key_prefix = f'"{node["key"]}": ' - - if not node.get('children'): - if node.get('complete'): - return f"{key_prefix}{{}}" - else: - return f"{key_prefix}{{" - - parts = [f"{key_prefix}{{"] - - children = node.get('children', []) - for i, child in enumerate(children): - child_rendered = self._renderNode(child, valuesWithBudget, indent + 1) - - # Add comma if not last and next sibling exists - if i < len(children) - 1: - if child.get('type') != 'truncated_value': - parts.append(f"{inner_indent}{child_rendered},") - else: - parts.append(f"{inner_indent}{child_rendered}") - else: - parts.append(f"{inner_indent}{child_rendered}") - - if node.get('complete'): - parts.append(f"{indent_str}}}") - - return '\n'.join(parts) - - def _renderArray(self, node: dict, valuesWithBudget: set, indent: int) -> str: - """Render an array node""" - indent_str = " " * indent - inner_indent = " " * (indent + 1) - - key_prefix = "" - if node.get('key'): - key_prefix = f'"{node["key"]}": ' - - if not node.get('children'): - if node.get('complete'): - return f"{key_prefix}[]" - else: - return f"{key_prefix}[" - - parts = [f"{key_prefix}["] - - children = node.get('children', []) - for i, child in enumerate(children): - child_rendered = self._renderNode(child, valuesWithBudget, indent + 1) - - if i < len(children) - 1: - if child.get('type') != 'truncated_value': - parts.append(f"{inner_indent}{child_rendered},") - else: - parts.append(f"{inner_indent}{child_rendered}") - else: - parts.append(f"{inner_indent}{child_rendered}") - - if node.get('complete'): - parts.append(f"{indent_str}]") - - return '\n'.join(parts) - - def _renderValue(self, node: dict, valuesWithBudget: set, indent: int) -> str: - """Render a value node""" - key_prefix = "" - if node.get('key'): - key_prefix = f'"{node["key"]}": ' - - if id(node) in valuesWithBudget: - # Full value - default_raw = '"...\"' - raw_value = node.get('raw', default_raw) - return f"{key_prefix}{raw_value}" - else: - # Placeholder - return f'{key_prefix}"..."' - - def _renderWithBudgetV2(self) -> str: - """ - Generate hierarchy context with budget logic. - - Alternative rendering that stays closer to the original truncated string. - Shows full context near the cut, replaces distant values with "...". - - Budget-Logik: - 1. Sammeln: Alle String-Werte werden mit ihrer Position gesammelt - 2. Sortieren: Nach Entfernung zum Cut-Punkt (näher = höhere Priorität) - 3. Zuweisen: Budget wird von hinten nach vorne aufgebraucht - 4. Ersetzen: Werte außerhalb des Budgets werden durch "..." ersetzt - """ - # Parse to understand structure, but render from original string with modifications - structure = self._parseForHierarchy() - - # Collect all complete value nodes with positions - allValues = self._collectCompleteValues(structure) - - # Sort by end position (furthest from cut = first to be truncated) - allValues.sort(key=lambda x: x['end_pos']) - - # Apply budget: replace values from the start until budget exhausted - budgetUsed = 0 - totalAvailable = sum(len(v['raw']) for v in allValues) - - valuesToReplace = [] - - for val in allValues: - valSize = len(val['raw']) - if totalAvailable - budgetUsed > self.budgetLimit: - # This value should be replaced with "..." - valuesToReplace.append(val) - budgetUsed += valSize - else: - break - - # Build the modified string - result = self.jsonStr - - # Replace from end to start to preserve positions - valuesToReplace.sort(key=lambda x: x['start_pos'], reverse=True) - - for val in valuesToReplace: - start = val['start_pos'] - end = val['end_pos'] - result = result[:start] + '"..."' + result[end:] - - return result - - def _collectCompleteValues(self, node: dict) -> list: - """Collect all complete (non-truncated) value nodes""" - values = [] - - if node.get('type') == 'value' and node.get('value_type') == 'string': - values.append({ - 'start_pos': node['start_pos'], - 'end_pos': node['end_pos'], - 'raw': node['raw'], - 'key': node.get('key') - }) - - for child in node.get('children', []): - values.extend(self._collectCompleteValues(child)) - - return values - - -def extractContinuationContexts( - truncatedJson: str -) -> Tuple[str, str, str]: - """ - Main entry point: Extract all three continuation contexts from a truncated JSON. - - Generiert drei Kontexte für abgeschnittene JSON-Strings: - 1. Overlap Context: Das innerste Objekt/Array-Element, das den Cut-Punkt enthält - - Wird verwendet, um den abgeschnittenen Teil mit dem neuen Teil zu mergen - - Exakt so wie im Original-String (für String-Matching beim Merge) - - 2. Hierarchy Context: Die hierarchische Struktur vom Root bis zum Cut-Punkt - - Mit Budget-Logik: Näher am Cut = vollständige Werte, weiter weg = "..." Platzhalter - - Gibt der AI den Kontext der gesamten JSON-Struktur - - 3. Complete Part: Der vollständige, valide JSON bis zum Cut-Punkt - - Alle offenen Strukturen werden geschlossen (}, ], ") - - Unvollständige Keys werden entfernt - - Kann direkt als valides JSON geparst werden - - Uses module constants BUDGET_LIMIT and OVERLAP_MAX_CHARS. - - Args: - truncatedJson: The truncated JSON string - - Returns: - Tuple of (overlapContext, hierarchyContext, completePart): - - overlapContext: The innermost object/element containing the cut (for merging) - - hierarchyContext: Full structure from root to cut with budget-limited values - - completePart: Valid JSON with all structures properly closed - - Example: - >>> jsonStr = '{"users": [{"name": "John", "bio": "Hello Wor' - >>> overlap, hierarchy, complete = extractContinuationContexts(jsonStr) - >>> import json - >>> parsed = json.loads(complete) # ✓ Funktioniert! - """ - return getJsonContinuationContext(truncatedJson) - - -# Convenience function with named results -def getContexts( - truncatedJson: str -) -> JsonContinuationContexts: - """ - Get all contexts as a Pydantic model with named fields. - - Uses module constants BUDGET_LIMIT and OVERLAP_MAX_CHARS. - - Args: - truncatedJson: The truncated JSON string - - Returns: - JsonContinuationContexts Pydantic model with: - - overlapContext: The innermost object/element containing the cut - - hierarchyContext: Full structure with budget-limited values - - completePart: Valid JSON with all structures properly closed - - Example: - >>> json_str = '{"users": [{"name": "John", "bio": "Hello Wor' - >>> contexts = getContexts(json_str) - >>> print(contexts.overlapContext) - >>> print(contexts.hierarchyContext) - >>> print(contexts.completePart) - """ - overlap, hierarchy, completePart = extractContinuationContexts(truncatedJson) - return JsonContinuationContexts( - overlapContext=overlap, - hierarchyContext=hierarchy, - completePart=completePart - ) diff --git a/modules/shared/jsonContinuation.md b/modules/shared/jsonContinuation-logic.md similarity index 100% rename from modules/shared/jsonContinuation.md rename to modules/shared/jsonContinuation-logic.md diff --git a/modules/shared/jsonContinuation.py b/modules/shared/jsonContinuation.py index d13bf033..324b133d 100644 --- a/modules/shared/jsonContinuation.py +++ b/modules/shared/jsonContinuation.py @@ -32,11 +32,16 @@ Autor: Claude Version: 2.0 """ +import json +import logging +import re from typing import Tuple, List, Optional, Any, Set from dataclasses import dataclass, field from enum import Enum from modules.datamodels.datamodelAi import JsonContinuationContexts +logger = logging.getLogger(__name__) + # ============================================================================= # MODULE CONSTANTS @@ -1442,12 +1447,7 @@ class JsonAnalyzer: return '' def _renderObjectV3(self, node: dict, depth: int, allocation: BudgetAllocation) -> str: - """ - Render object. - - PATH containers: always render with structure - - NON-PATH containers: only render as if summary_mode AND no allocated children - - Otherwise: render with full details (children may have allocated values) - """ + """Render object - summary mode non-path objects become .""" indentStr = " " * depth innerIndent = " " * (depth + 1) @@ -1455,26 +1455,19 @@ class JsonAnalyzer: children = node.get('children', []) isOnPath = id(node) in allocation.path_node_ids - # Check if any child has allocated values - hasAllocatedChildren = False - if children: - for child in children: - if child.get('type') in ('value', 'truncated_value'): - if id(child) in allocation.allocated_node_ids: - hasAllocatedChildren = True - break - else: - # For containers, check recursively if they have allocated descendants - if self._hasAllocatedDescendants(child, allocation): - hasAllocatedChildren = True - break - - # NON-PATH containers: only render as if summary_mode AND no allocated children - # PATH containers always render with structure - if not isOnPath and allocation.summary_mode and not hasAllocatedChildren: + if allocation.summary_mode and not isOnPath: return f"{keyPrefix}" - # Render with details (either PATH container or NON-PATH with allocated children) + # If object is incomplete and cut is directly here (no incomplete child), + # extract exact string from original JSON to preserve formatting + if not node.get('complete') and node.get('start_pos') is not None: + hasIncompleteChild = any( + child.get('type') in ('object', 'array') and not child.get('complete') + for child in children + ) + if not hasIncompleteChild: + return self.jsonStr[node.get('start_pos'):] + if not children: return f"{keyPrefix}{{}}" if node.get('complete') else f"{keyPrefix}{{" @@ -1495,26 +1488,8 @@ class JsonAnalyzer: return '\n'.join(parts) - def _hasAllocatedDescendants(self, node: dict, allocation: BudgetAllocation) -> bool: - """Check if node or any of its descendants have allocated values.""" - nodeType = node.get('type') - - if nodeType in ('value', 'truncated_value'): - return id(node) in allocation.allocated_node_ids - - for child in node.get('children', []): - if self._hasAllocatedDescendants(child, allocation): - return True - - return False - def _renderArrayV3(self, node: dict, depth: int, allocation: BudgetAllocation) -> str: - """ - Render array. - - PATH containers: always render with structure - - NON-PATH containers: only render as if summary_mode AND no allocated children - - Otherwise: render with full details (children may have allocated values) - """ + """Render array - summary mode non-path arrays become .""" indentStr = " " * depth innerIndent = " " * (depth + 1) @@ -1522,26 +1497,19 @@ class JsonAnalyzer: children = node.get('children', []) isOnPath = id(node) in allocation.path_node_ids - # Check if any child has allocated values - hasAllocatedChildren = False - if children: - for child in children: - if child.get('type') in ('value', 'truncated_value'): - if id(child) in allocation.allocated_node_ids: - hasAllocatedChildren = True - break - else: - # For containers, check recursively if they have allocated descendants - if self._hasAllocatedDescendants(child, allocation): - hasAllocatedChildren = True - break - - # NON-PATH containers: only render as if summary_mode AND no allocated children - # PATH containers always render with structure - if not isOnPath and allocation.summary_mode and not hasAllocatedChildren: + if allocation.summary_mode and not isOnPath: return f"{keyPrefix}" - # Render with details (either PATH container or NON-PATH with allocated children) + # If array is incomplete and cut is directly here (no incomplete child), + # extract exact string from original JSON to preserve formatting + if not node.get('complete') and node.get('start_pos') is not None: + hasIncompleteChild = any( + child.get('type') in ('object', 'array') and not child.get('complete') + for child in children + ) + if not hasIncompleteChild: + return self.jsonStr[node.get('start_pos'):] + if not children: return f"{keyPrefix}[]" if node.get('complete') else f"{keyPrefix}[" @@ -1669,6 +1637,276 @@ def extractContinuationContexts( return getJsonContinuationContext(truncatedJson) +# ============================================================================= +# JSON REPAIR FUNCTIONS +# ============================================================================= + +def _repairInternalJsonErrors(jsonStr: str) -> str: + """ + Repair internal JSON errors WITHOUT touching incomplete structures at cut point. + + This function fixes common internal JSON issues: + - Invalid escape sequences (e.g., \\x, \\u without proper hex) + - Unescaped control characters + - Invalid Unicode characters + - Trailing commas before closing brackets/braces + - Comments (// and /* */) + - Single quotes instead of double quotes (outside of string values) + - Unquoted keys + + IMPORTANT: Does NOT modify incomplete structures at the end of the JSON. + Those are handled separately by structure closing logic. + + Args: + jsonStr: JSON string that may have internal errors + + Returns: + Repaired JSON string with internal errors fixed + """ + if not jsonStr or not jsonStr.strip(): + return jsonStr + + result = jsonStr + + # Fix 1: Remove BOM and normalize whitespace at start + if result.startswith('\ufeff'): + result = result[1:] + + # Fix 2: Normalize smart quotes to straight quotes + result = result.replace('"', '"').replace('"', '"') + result = result.replace(''', "'").replace(''', "'") + + # Fix 3: Remove JavaScript-style comments (but be careful not to break strings) + result = _removeJsonComments(result) + + # Fix 4: Fix invalid escape sequences + result = _fixInvalidEscapeSequences(result) + + # Fix 5: Remove trailing commas before ] or } + result = _removeTrailingCommas(result) + + # Fix 6: Fix unquoted keys (simple cases only) + result = _fixUnquotedKeys(result) + + return result + + +def _removeJsonComments(jsonStr: str) -> str: + """Remove JavaScript-style comments from JSON, preserving strings.""" + result = [] + i = 0 + inString = False + escaped = False + + while i < len(jsonStr): + char = jsonStr[i] + + if escaped: + result.append(char) + escaped = False + i += 1 + continue + + if char == '\\' and inString: + result.append(char) + escaped = True + i += 1 + continue + + if char == '"': + inString = not inString + result.append(char) + i += 1 + continue + + if inString: + result.append(char) + i += 1 + continue + + # Check for // comment + if char == '/' and i + 1 < len(jsonStr) and jsonStr[i + 1] == '/': + # Skip until end of line + while i < len(jsonStr) and jsonStr[i] != '\n': + i += 1 + continue + + # Check for /* */ comment + if char == '/' and i + 1 < len(jsonStr) and jsonStr[i + 1] == '*': + i += 2 + while i + 1 < len(jsonStr): + if jsonStr[i] == '*' and jsonStr[i + 1] == '/': + i += 2 + break + i += 1 + continue + + result.append(char) + i += 1 + + return ''.join(result) + + +def _fixInvalidEscapeSequences(jsonStr: str) -> str: + """Fix invalid escape sequences in JSON strings.""" + result = [] + i = 0 + inString = False + + while i < len(jsonStr): + char = jsonStr[i] + + if char == '"' and (i == 0 or jsonStr[i - 1] != '\\'): + inString = not inString + result.append(char) + i += 1 + continue + + if inString and char == '\\' and i + 1 < len(jsonStr): + nextChar = jsonStr[i + 1] + + # Valid JSON escape sequences: \", \\, \/, \b, \f, \n, \r, \t, \uXXXX + validEscapes = ['"', '\\', '/', 'b', 'f', 'n', 'r', 't', 'u'] + + if nextChar in validEscapes: + if nextChar == 'u': + # Check if followed by 4 hex digits + if i + 5 < len(jsonStr) and all(c in '0123456789abcdefABCDEF' for c in jsonStr[i + 2:i + 6]): + result.append(char) + i += 1 + continue + else: + # Invalid \u sequence - escape the backslash + result.append('\\') + result.append('\\') + i += 1 + continue + else: + result.append(char) + i += 1 + continue + else: + # Invalid escape - escape the backslash + result.append('\\') + result.append('\\') + i += 1 + continue + + result.append(char) + i += 1 + + return ''.join(result) + + +def _removeTrailingCommas(jsonStr: str) -> str: + """Remove trailing commas before ] or } (not valid in JSON).""" + # Pattern: comma followed by whitespace and ] or } + result = re.sub(r',(\s*[}\]])', r'\1', jsonStr) + return result + + +def _fixUnquotedKeys(jsonStr: str) -> str: + """ + Fix simple unquoted keys in JSON objects. + Only handles simple cases to avoid breaking valid JSON. + """ + # Pattern: { or , followed by whitespace and an unquoted identifier and : + # Be conservative - only fix clear cases + + result = [] + i = 0 + inString = False + escaped = False + + while i < len(jsonStr): + char = jsonStr[i] + + if escaped: + result.append(char) + escaped = False + i += 1 + continue + + if char == '\\' and inString: + result.append(char) + escaped = True + i += 1 + continue + + if char == '"': + inString = not inString + result.append(char) + i += 1 + continue + + if inString: + result.append(char) + i += 1 + continue + + # Check for unquoted key after { or , + if char in '{,' and i + 1 < len(jsonStr): + result.append(char) + i += 1 + + # Skip whitespace + while i < len(jsonStr) and jsonStr[i] in ' \t\n\r': + result.append(jsonStr[i]) + i += 1 + + if i >= len(jsonStr): + continue + + # Check if next is an unquoted identifier (starts with letter or _) + if jsonStr[i] not in '"{[' and (jsonStr[i].isalpha() or jsonStr[i] == '_'): + # Collect the identifier + keyStart = i + while i < len(jsonStr) and (jsonStr[i].isalnum() or jsonStr[i] == '_'): + i += 1 + key = jsonStr[keyStart:i] + + # Skip whitespace + while i < len(jsonStr) and jsonStr[i] in ' \t\n\r': + i += 1 + + # Check if followed by : + if i < len(jsonStr) and jsonStr[i] == ':': + # This was an unquoted key - quote it + result.append('"') + result.append(key) + result.append('"') + else: + # Not a key, put back as-is + result.append(key) + continue + + result.append(char) + i += 1 + + return ''.join(result) + + +def _tryParseJson(jsonStr: str) -> tuple: + """ + Try to parse JSON string and return (parsed, error). + + Returns: + Tuple of (parsed_object, error_string) + - If successful: (parsed_object, None) + - If failed: (None, error_message) + """ + if not jsonStr or not jsonStr.strip(): + return None, "Empty JSON string" + + try: + parsed = json.loads(jsonStr) + return parsed, None + except json.JSONDecodeError as e: + return None, str(e) + except Exception as e: + return None, str(e) + + # Convenience function with named results def getContexts( truncatedJson: str @@ -1678,28 +1916,86 @@ def getContexts( Uses module constants BUDGET_LIMIT and OVERLAP_MAX_CHARS. + This function: + 1. Extracts continuation contexts (overlap, hierarchy, completePart) + 2. Tries to parse completePart as JSON + 3. If parsing fails, repairs internal errors and retries + 4. Sets jsonParsingSuccess to indicate if completePart is valid JSON + 5. Sets overlapContext="" if JSON is complete (no cut point) + + IMPORTANT: overlapContext="" signals that JSON is complete (no more data expected). + This happens when the original JSON is already valid (no structures needed closing). + Args: truncatedJson: The truncated JSON string Returns: JsonContinuationContexts Pydantic model with: - overlapContext: The innermost object/element containing the cut + Empty string "" if JSON is complete (no cut point) - hierarchyContext: Full structure WITHOUT budget limitations (for internal use) - hierarchyContextForPrompt: Full structure WITH budget limitations (for prompts) - completePart: Valid JSON with all structures properly closed + - jsonParsingSuccess: True if completePart is valid parseable JSON Example: >>> json_str = '{"users": [{"name": "John", "bio": "Hello Wor' >>> contexts = getContexts(json_str) - >>> print(contexts.overlapContext) - >>> print(contexts.hierarchyContext) - >>> print(contexts.hierarchyContextForPrompt) - >>> print(contexts.completePart) + >>> print(contexts.overlapContext) # Contains cut point context + >>> print(contexts.jsonParsingSuccess) + + >>> complete_json = '{"users": [{"name": "John"}]}' + >>> contexts = getContexts(complete_json) + >>> print(contexts.overlapContext) # "" (empty - JSON is complete) + >>> print(contexts.jsonParsingSuccess) # True """ + # First, check if original JSON is already complete (parseable without modification) + jsonIsComplete = False + if truncatedJson and truncatedJson.strip(): + parsed, error = _tryParseJson(truncatedJson.strip()) + if error is None: + jsonIsComplete = True + logger.debug("Original JSON is already complete (no cut point)") + + # Extract contexts overlap, hierarchy, hierarchyForPrompt, completePart = extractContinuationContexts(truncatedJson) + + # If JSON is complete (no cut point), set overlapContext to empty string + # This signals that no more continuation is needed + if jsonIsComplete: + overlap = "" + logger.debug("Setting overlapContext='' (JSON is complete)") + + # Try to parse completePart as JSON + jsonParsingSuccess = False + + if completePart and completePart.strip(): + # First attempt: parse as-is + parsed, error = _tryParseJson(completePart) + + if error is None: + jsonParsingSuccess = True + else: + # Second attempt: repair internal errors and retry + logger.debug(f"Initial parse failed: {error}, attempting repair") + repairedCompletePart = _repairInternalJsonErrors(completePart) + + parsed, error = _tryParseJson(repairedCompletePart) + + if error is None: + # Repair succeeded - use repaired version + completePart = repairedCompletePart + jsonParsingSuccess = True + logger.debug("JSON repair successful") + else: + # Repair also failed - keep original completePart, mark as failed + logger.debug(f"JSON repair also failed: {error}") + jsonParsingSuccess = False + return JsonContinuationContexts( overlapContext=overlap, hierarchyContext=hierarchy, hierarchyContextForPrompt=hierarchyForPrompt, - completePart=completePart + completePart=completePart, + jsonParsingSuccess=jsonParsingSuccess ) diff --git a/tests/functional/test12_json_split_merge.py b/tests/functional/test12_json_split_merge.py index fc9c4c63..4dac56cb 100644 --- a/tests/functional/test12_json_split_merge.py +++ b/tests/functional/test12_json_split_merge.py @@ -126,170 +126,97 @@ class JsonSplitMergeTester12: ] } }, - { - "name": "large_customers.json", - "data": self._createLargeCustomersData() - }, - { - "name": "large_products.json", - "data": self._createLargeProductsData() - }, - { - "name": "large_documents.json", - "data": self._createLargeDocumentsData() - }, { "name": "table_example.json", "data": self._loadTableJsonExample() + }, + { + "name": "complete_json.json", + "data": { + "status": "complete", + "message": "This is a complete, valid JSON object", + "data": { + "items": [1, 2, 3, 4, 5], + "metadata": { + "version": "1.0", + "timestamp": "2025-01-05T12:00:00Z" + } + } + }, + "isComplete": True # Flag to indicate this is complete JSON (not cut) + }, + { + "name": "json_with_comments.json", + "data": None, # Will be set as string with comments + "jsonString": '''{ + // This is a single-line comment + "name": "Test", + "value": 42, + /* This is a multi-line comment + spanning multiple lines */ + "items": [1, 2, 3], + "nested": { + // Another comment + "key": "value" + } + }''', + "hasComments": True + }, + { + "name": "json_with_trailing_comma.json", + "data": None, # Will be set as string with trailing comma + "jsonString": '''{ + "name": "Test", + "value": 42, + "items": [1, 2, 3,], + "nested": { + "key": "value", + } + }''', + "hasTrailingComma": True + }, + { + "name": "json_with_unquoted_keys.json", + "data": None, # Will be set as string with unquoted keys + "jsonString": '''{ + name: "Test", + value: 42, + items: [1, 2, 3], + nested: { + key: "value" + } + }''', + "hasUnquotedKeys": True + }, + { + "name": "json_with_invalid_escape.json", + "data": None, # Will be set as string with invalid escape + "jsonString": '''{ + "name": "Test\\xInvalid", + "value": 42, + "description": "This has \\u invalid escape" + }''', + "hasInvalidEscape": True + }, + { + "name": "json_mixed_errors.json", + "data": None, # Will be set as string with multiple errors + "jsonString": '''{ + // Comment here + name: "Test", // Unquoted key + "value": 42, + "items": [1, 2, 3,], // Trailing comma + "description": "Has \\x invalid escape", + "nested": { + key: "value", // Unquoted key and trailing comma + } + }''', + "hasMixedErrors": True } ] return testFiles - def _createLargeCustomersData(self) -> Dict[str, Any]: - """Create a large customers dataset for budget testing.""" - customers = [] - # Create 100 customers with long descriptions - for i in range(100): - customers.append({ - "id": i + 1, - "name": f"Customer {i + 1}", - "email": f"customer{i+1}@example.com", - "phone": f"+1{5550000000 + i}", - "address": f"{100 + i} Main Street, City {i % 10}, State {i % 5}, ZIP {10000 + i}", - "description": f"This is a detailed description for customer {i + 1}. " * 10 + - f"They have been a loyal customer since {2000 + (i % 25)}. " + - f"Their preferences include various products and services. " * 5, - "orders": [ - { - "orderId": f"ORD-{i+1}-{j+1}", - "date": f"2024-{(j % 12) + 1:02d}-{(j % 28) + 1:02d}", - "total": round(100.0 + (i * 10) + (j * 5), 2), - "items": [ - { - "productId": f"PROD-{k+1}", - "quantity": (k % 5) + 1, - "price": round(10.0 + k * 2, 2) - } - for k in range(3) - ] - } - for j in range(5) - ], - "metadata": { - "created": f"2020-{(i % 12) + 1:02d}-{(i % 28) + 1:02d}", - "lastLogin": f"2024-{(i % 12) + 1:02d}-{(i % 28) + 1:02d}", - "tags": [f"tag-{i % 10}", f"category-{i % 5}", f"segment-{i % 3}"] - } - }) - return {"customers": customers} - - def _createLargeProductsData(self) -> Dict[str, Any]: - """Create a large products dataset for budget testing.""" - products = [] - # Create 200 products with detailed information - categories = ["Electronics", "Clothing", "Books", "Home & Garden", "Sports", "Toys", "Automotive", "Health"] - for i in range(200): - category = categories[i % len(categories)] - products.append({ - "id": f"PROD-{i+1:04d}", - "name": f"Product {i+1} - {category}", - "category": category, - "price": round(10.0 + (i * 2.5), 2), - "cost": round(5.0 + (i * 1.5), 2), - "inStock": i % 3 != 0, - "stockQuantity": (i % 100) * 10, - "description": f"This is a comprehensive product description for Product {i+1}. " * 15 + - f"It belongs to the {category} category and offers excellent value. " * 10 + - f"Features include: feature-1, feature-2, feature-3, and many more. " * 5, - "specifications": { - "weight": f"{1.0 + (i % 10)} kg", - "dimensions": f"{10 + (i % 20)}x{5 + (i % 15)}x{3 + (i % 10)} cm", - "color": ["red", "blue", "green", "black", "white"][i % 5], - "material": ["plastic", "metal", "wood", "fabric"][i % 4], - "warranty": f"{1 + (i % 5)} years" - }, - "reviews": [ - { - "userId": f"USER-{j+1}", - "rating": (j % 5) + 1, - "comment": f"Review {j+1} for product {i+1}: " + "This is a detailed review comment. " * 10, - "date": f"2024-{(j % 12) + 1:02d}-{(j % 28) + 1:02d}" - } - for j in range(3) - ], - "relatedProducts": [f"PROD-{k+1:04d}" for k in range(max(0, i-2), min(200, i+3)) if k != i] - }) - return {"products": products} - - def _createLargeDocumentsData(self) -> Dict[str, Any]: - """Create a large documents dataset for budget testing.""" - documents = [] - # Create 50 documents with nested structures - for i in range(50): - sections = [] - for j in range(10): - elements = [] - for k in range(5): - if k % 2 == 0: - elements.append({ - "type": "heading", - "level": (k % 3) + 1, - "content": { - "text": f"Section {j+1} Heading {k+1} for Document {i+1}", - "style": "bold" - } - }) - else: - elements.append({ - "type": "paragraph", - "content": { - "text": f"This is paragraph {k+1} in section {j+1} of document {i+1}. " * 20 + - f"It contains detailed information about various topics. " * 15 + - f"The content is structured and well-organized. " * 10 - } - }) - - sections.append({ - "id": f"sec-{i+1}-{j+1}", - "title": f"Section {j+1}", - "content_type": "mixed", - "elements": elements, - "metadata": { - "created": f"2024-{(j % 12) + 1:02d}-{(j % 28) + 1:02d}", - "modified": f"2024-{(j % 12) + 1:02d}-{(j % 28) + 1:02d}", - "author": f"Author-{(i % 10) + 1}", - "tags": [f"tag-{j % 10}", f"category-{i % 5}"] - } - }) - - documents.append({ - "id": f"doc-{i+1:03d}", - "title": f"Document {i+1} - Comprehensive Report", - "description": f"This is a comprehensive document with detailed information. " * 30 + - f"It covers multiple topics and sections. " * 20 + - f"The content is extensive and well-structured. " * 15, - "sections": sections, - "metadata": { - "created": f"2024-{(i % 12) + 1:02d}-{(i % 28) + 1:02d}", - "modified": f"2024-{(i % 12) + 1:02d}-{(i % 28) + 1:02d}", - "author": f"Author-{(i % 10) + 1}", - "version": f"1.{(i % 10)}", - "status": ["draft", "review", "published"][i % 3], - "tags": [f"tag-{i % 20}" for _ in range(5)] - } - }) - - return { - "metadata": { - "title": "Large Document Collection", - "description": "A collection of 50 comprehensive documents for testing budget limits", - "totalDocuments": 50, - "created": "2024-01-01" - }, - "documents": documents - } - def _loadTableJsonExample(self) -> Dict[str, Any]: """Load the table JSON example from the debug prompts file.""" try: @@ -460,23 +387,51 @@ class JsonSplitMergeTester12: async def testJsonSplitMerge(self, jsonFile: Dict[str, Any]) -> Dict[str, Any]: """Test splitting and merging a single JSON file.""" fileName = jsonFile["name"] - originalData = jsonFile["data"] + + # Check if this is a complete JSON test (no cut) + isComplete = jsonFile.get("isComplete", False) + + # Check if this is a JSON string with errors (not from data dict) + jsonString = jsonFile.get("jsonString") + if jsonString: + # Use the provided JSON string directly (may have errors) + originalJsonString = jsonString + originalData = None # No original data for error tests + else: + # Convert data dict to JSON string + originalData = jsonFile["data"] + originalJsonString = json.dumps(originalData, indent=2, ensure_ascii=False) + + originalSize = len(originalJsonString) self._log("") self._log("="*80) - self._log(f"TESTING JSON SPLIT AND MERGE: {fileName}") + testType = "COMPLETE JSON" if isComplete else ("JSON WITH ERRORS" if jsonString else "SPLIT JSON") + self._log(f"TESTING {testType}: {fileName}") self._log("="*80) - # Convert to JSON string - originalJsonString = json.dumps(originalData, indent=2, ensure_ascii=False) - originalSize = len(originalJsonString) - # Log original JSON self._log("") self._log("="*80) self._log("ORIGINAL JSON") self._log("="*80) self._log(f"JSON length: {originalSize} characters") + if isComplete: + self._log(" ⚠️ This is COMPLETE JSON (not cut) - testing overlapContext='' detection") + if jsonString: + errorType = [] + if jsonFile.get("hasComments"): + errorType.append("comments") + if jsonFile.get("hasTrailingComma"): + errorType.append("trailing commas") + if jsonFile.get("hasUnquotedKeys"): + errorType.append("unquoted keys") + if jsonFile.get("hasInvalidEscape"): + errorType.append("invalid escapes") + if jsonFile.get("hasMixedErrors"): + errorType.append("mixed errors") + if errorType: + self._log(f" ⚠️ This JSON has errors: {', '.join(errorType)} - testing repair function") self._log("") self._log("Full JSON content:") self._log("-"*80) @@ -491,39 +446,63 @@ class JsonSplitMergeTester12: for line in jsonLines: self._log(line) - # Split JSON at random position (simulating AI response cut) - self._log("") - self._log("="*80) - self._log("SPLITTING JSON AT RANDOM POSITION (SIMULATING AI RESPONSE CUT)") - self._log("="*80) - - # Find random cut position (not at start or end) - import random - minCutPos = max(100, originalSize // 10) # At least 10% from start - maxCutPos = min(originalSize - 100, originalSize * 9 // 10) # At least 10% from end - cutPosition = random.randint(minCutPos, maxCutPos) - - # Get part from start to cut - partContent = originalJsonString[:cutPosition] - - self._log("") - self._log("="*80) - self._log("PART (from start to cut):") - self._log("="*80) - self._log(f"Cut position: {cutPosition} characters") - self._log(f"Part length: {len(partContent)} characters") - self._log("") - self._log("Part content:") - partLines = partContent.split('\n') - if len(partLines) > 30: - for line in partLines[:15]: - self._log(f" {line}") - self._log(f" ... ({len(partLines) - 30} lines omitted) ...") - for line in partLines[-15:]: - self._log(f" {line}") + # Handle complete JSON, JSON with errors, vs split JSON + if isComplete or jsonString: + # For complete JSON or JSON with errors, use the full string (no cut) + # We want to test repair on the full error-containing JSON + partContent = originalJsonString + cutPosition = None # No cut + self._log("") + self._log("="*80) + if isComplete: + self._log("COMPLETE JSON TEST (NO CUT)") + self._log("="*80) + self._log(" Testing that getContexts() detects complete JSON and sets overlapContext=''") + else: + self._log("JSON WITH ERRORS TEST (NO CUT)") + self._log("="*80) + self._log(" Testing that getContexts() repairs the errors and produces valid JSON") else: - for line in partLines: - self._log(f" {line}") + # Split JSON at random position (simulating AI response cut) + self._log("") + self._log("="*80) + self._log("SPLITTING JSON AT RANDOM POSITION (SIMULATING AI RESPONSE CUT)") + self._log("="*80) + + # Find random cut position (not at start or end) + import random + minCutPos = max(100, originalSize // 10) # At least 10% from start + maxCutPos = min(originalSize - 100, originalSize * 9 // 10) # At least 10% from end + + # Ensure valid range + if maxCutPos <= minCutPos: + # For small JSON, just cut in the middle + cutPosition = originalSize // 2 + else: + cutPosition = random.randint(minCutPos, maxCutPos) + + # Get part from start to cut + partContent = originalJsonString[:cutPosition] + + if not isComplete: + self._log("") + self._log("="*80) + self._log("PART (from start to cut):") + self._log("="*80) + self._log(f"Cut position: {cutPosition} characters") + self._log(f"Part length: {len(partContent)} characters") + self._log("") + self._log("Part content:") + partLines = partContent.split('\n') + if len(partLines) > 30: + for line in partLines[:15]: + self._log(f" {line}") + self._log(f" ... ({len(partLines) - 30} lines omitted) ...") + for line in partLines[-15:]: + self._log(f" {line}") + else: + for line in partLines: + self._log(f" {line}") # Generate contexts using getContexts() self._log("") @@ -590,12 +569,23 @@ class JsonSplitMergeTester12: for line in completeLines: self._log(f" {line}") - # Validate completePart as JSON + # Validate completePart as JSON and check overlapContext self._log("") self._log("="*80) - self._log("VALIDATING COMPLETE PART AS JSON:") + self._log("VALIDATION RESULTS:") self._log("="*80) + # Check overlapContext for complete JSON + if isComplete: + if contexts.overlapContext == "": + self._log(" ✅ overlapContext is empty (correct for complete JSON)") + else: + self._log(f" ❌ overlapContext is NOT empty: '{contexts.overlapContext[:50]}...'") + self._log(" Expected empty string for complete JSON") + + # Validate completePart as JSON + self._log("") + self._log("VALIDATING COMPLETE PART AS JSON:") isValidJson = False parsedCompletePart = None jsonError = None @@ -606,34 +596,40 @@ class JsonSplitMergeTester12: self._log(" ✅ completePart is valid JSON") self._log(f" Parsed type: {type(parsedCompletePart).__name__}") - # Compare with truncated JSON (not original) - parse the truncated part to compare - from modules.shared.jsonUtils import closeJsonStructures, tryParseJson + # For error tests, verify repair worked + if jsonString: + self._log(" ✅ JSON repair successful - errors were fixed") - # Try to parse the truncated JSON part (with structures closed) - truncatedClosed = closeJsonStructures(partContent) - truncatedParsed, truncatedError, _ = tryParseJson(truncatedClosed) - - if truncatedParsed is not None: - # Compare completePart with the parsed truncated JSON - if isinstance(parsedCompletePart, dict) and isinstance(truncatedParsed, dict): - comparison = self.compareJson(truncatedParsed, parsedCompletePart) - self._log(f" Comparison with truncated JSON (at cut position {cutPosition}):") - self._log(f" Exact match: {comparison['exactMatch']}") - self._log(f" Size match: {comparison['sizeMatch']}") - if comparison['differences']: - self._log(f" Differences found: {len(comparison['differences'])}") - for diff in comparison['differences'][:10]: # Show first 10 differences - self._log(f" - {diff}") - if len(comparison['differences']) > 10: - self._log(f" ... ({len(comparison['differences']) - 10} more differences)") + # For split JSON, compare with truncated JSON + if not isComplete and not jsonString: + # Compare with truncated JSON (not original) - parse the truncated part to compare + from modules.shared.jsonUtils import closeJsonStructures, tryParseJson + + # Try to parse the truncated JSON part (with structures closed) + truncatedClosed = closeJsonStructures(partContent) + truncatedParsed, truncatedError, _ = tryParseJson(truncatedClosed) + + if truncatedParsed is not None: + # Compare completePart with the parsed truncated JSON + if isinstance(parsedCompletePart, dict) and isinstance(truncatedParsed, dict): + comparison = self.compareJson(truncatedParsed, parsedCompletePart) + self._log(f" Comparison with truncated JSON (at cut position {cutPosition}):") + self._log(f" Exact match: {comparison['exactMatch']}") + self._log(f" Size match: {comparison['sizeMatch']}") + if comparison['differences']: + self._log(f" Differences found: {len(comparison['differences'])}") + for diff in comparison['differences'][:10]: # Show first 10 differences + self._log(f" - {diff}") + if len(comparison['differences']) > 10: + self._log(f" ... ({len(comparison['differences']) - 10} more differences)") + else: + self._log(" No differences found - completePart matches truncated JSON structure") + elif isinstance(parsedCompletePart, list) and isinstance(truncatedParsed, list): + self._log(f" Both are lists: truncated={len(truncatedParsed)} items, completePart={len(parsedCompletePart)} items") else: - self._log(" No differences found - completePart matches truncated JSON structure") - elif isinstance(parsedCompletePart, list) and isinstance(truncatedParsed, list): - self._log(f" Both are lists: truncated={len(truncatedParsed)} items, completePart={len(parsedCompletePart)} items") + self._log(f" Different types: truncated={type(truncatedParsed).__name__}, completePart={type(parsedCompletePart).__name__}") else: - self._log(f" Different types: truncated={type(truncatedParsed).__name__}, completePart={type(parsedCompletePart).__name__}") - else: - self._log(f" Could not parse truncated JSON for comparison (error: {truncatedError})") + self._log(f" Could not parse truncated JSON for comparison (error: {truncatedError})") except json.JSONDecodeError as e: isValidJson = False @@ -641,13 +637,15 @@ class JsonSplitMergeTester12: self._log(f" ❌ completePart is NOT valid JSON") self._log(f" Error: {jsonError}") self._log(f" Error position: line {e.lineno}, column {e.colno}") + if jsonString: + self._log(" ❌ JSON repair FAILED - errors were not fixed") # Return test results - return { + result = { "success": isValidJson, "fileName": fileName, "originalSize": originalSize, - "cutPosition": cutPosition, + "cutPosition": cutPosition if not isComplete else None, "partSize": len(partContent), "overlapContextSize": len(contexts.overlapContext), "hierarchyContextSize": len(contexts.hierarchyContext), @@ -655,8 +653,23 @@ class JsonSplitMergeTester12: "completePartSize": len(contexts.completePart), "isValidJson": isValidJson, "jsonError": jsonError, - "parsedCompletePart": parsedCompletePart is not None + "parsedCompletePart": parsedCompletePart is not None, + "jsonParsingSuccess": contexts.jsonParsingSuccess } + + # Add complete JSON specific checks + if isComplete: + result["overlapContextIsEmpty"] = contexts.overlapContext == "" + result["isComplete"] = True + # For complete JSON, success means overlapContext is empty AND valid JSON + result["success"] = isValidJson and (contexts.overlapContext == "") + + # Add error test specific checks + if jsonString: + result["hasErrors"] = True + result["repairSuccess"] = isValidJson + + return result async def testAllJsonFiles(self) -> Dict[str, Any]: """Test splitting and merging all test JSON files.""" @@ -723,11 +736,31 @@ class JsonSplitMergeTester12: if result.get("success"): successCount += 1 isValidJson = result.get("isValidJson", False) - if isValidJson: - print(f"✅ {fileName:30s}: Valid JSON - completePart parsed successfully") + isComplete = result.get("isComplete", False) + hasErrors = result.get("hasErrors", False) + + if isComplete: + overlapEmpty = result.get("overlapContextIsEmpty", False) + if isValidJson and overlapEmpty: + print(f"✅ {fileName:30s}: Complete JSON - overlapContext='' and valid JSON") + elif not overlapEmpty: + print(f"⚠️ {fileName:30s}: Complete JSON but overlapContext not empty") + else: + jsonError = result.get("jsonError", "Unknown error") + print(f"⚠️ {fileName:30s}: Complete JSON but not valid - {jsonError}") + elif hasErrors: + repairSuccess = result.get("repairSuccess", False) + if repairSuccess: + print(f"✅ {fileName:30s}: JSON with errors - repair successful") + else: + jsonError = result.get("jsonError", "Unknown error") + print(f"❌ {fileName:30s}: JSON with errors - repair failed - {jsonError}") else: - jsonError = result.get("jsonError", "Unknown error") - print(f"⚠️ {fileName:30s}: Contexts generated but completePart is not valid JSON - {jsonError}") + if isValidJson: + print(f"✅ {fileName:30s}: Valid JSON - completePart parsed successfully") + else: + jsonError = result.get("jsonError", "Unknown error") + print(f"⚠️ {fileName:30s}: Contexts generated but completePart is not valid JSON - {jsonError}") else: error = result.get("error", "Unknown error") print(f"❌ {fileName:30s}: FAILED - {error}")