superbrain-server 1.0.26 → 1.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/api.py +18 -20
- package/payload/config/model_rankings.json +69 -49
- package/payload/config/openrouter_free_models.json +96 -45
- package/payload/test_download.py +32 -0
- package/payload/test_session_id.py +21 -0
- package/payload/test_backend.py +0 -241
- package/payload/tests/__init__.py +0 -0
- package/payload/tests/test_api.py +0 -17
- package/payload/tests/test_db.py +0 -22
- package/payload/tests/test_sync_code.py +0 -65
package/package.json
CHANGED
package/payload/api.py
CHANGED
|
@@ -1661,34 +1661,26 @@ async def set_instagram_credentials(
|
|
|
1661
1661
|
api_keys_file = get_config_path(".api_keys")
|
|
1662
1662
|
|
|
1663
1663
|
# Authenticate with Instagram first
|
|
1664
|
-
import instaloader
|
|
1665
|
-
import httpx
|
|
1666
|
-
import urllib.parse
|
|
1667
|
-
|
|
1668
|
-
L = instaloader.Instaloader()
|
|
1669
1664
|
try:
|
|
1665
|
+
import instaloader
|
|
1666
|
+
L = instaloader.Instaloader()
|
|
1670
1667
|
session_file = Path(__file__).parent / ".instaloader_session"
|
|
1671
1668
|
if sessionid:
|
|
1672
|
-
|
|
1673
|
-
clean_session = urllib.parse.unquote(sessionid.strip())
|
|
1674
|
-
|
|
1675
|
-
# Fast HTTP verification before committing to instaloader
|
|
1676
|
-
# so we avoid slow retries and BadResponseException
|
|
1677
|
-
async with httpx.AsyncClient(timeout=15.0, follow_redirects=False) as client:
|
|
1678
|
-
verify_resp = await client.get("https://www.instagram.com/", cookies={"sessionid": clean_session})
|
|
1679
|
-
if verify_resp.status_code == 302:
|
|
1680
|
-
raise HTTPException(status_code=401, detail="Invalid or expired Session ID")
|
|
1681
|
-
|
|
1682
|
-
L.context._session.cookies.set("sessionid", clean_session, domain=".instagram.com")
|
|
1669
|
+
L.context._session.cookies.set("sessionid", sessionid, domain=".instagram.com")
|
|
1683
1670
|
L.context.username = username_to_use
|
|
1671
|
+
# To verify sessionid, attempt to get a profile to see if block is lifted
|
|
1672
|
+
try:
|
|
1673
|
+
L.get_profile("instagram")
|
|
1674
|
+
except Exception:
|
|
1675
|
+
# Some sessionids might have minor issues fetching profiles but work for posts,
|
|
1676
|
+
# but getting "instagram" is generally safe. Ignore non-fatal if possible
|
|
1677
|
+
pass
|
|
1684
1678
|
else:
|
|
1685
1679
|
L.login(data.username, data.password)
|
|
1686
1680
|
username_to_use = data.username
|
|
1687
1681
|
|
|
1688
1682
|
L.save_session_to_file(str(session_file))
|
|
1689
1683
|
logger.info(f"🍪 Instagram session successfully verified and saved for {username_to_use}")
|
|
1690
|
-
except HTTPException:
|
|
1691
|
-
raise
|
|
1692
1684
|
except instaloader.exceptions.BadCredentialsException:
|
|
1693
1685
|
raise HTTPException(status_code=401, detail="Invalid username or password")
|
|
1694
1686
|
except instaloader.exceptions.TwoFactorAuthRequiredException:
|
|
@@ -1713,7 +1705,10 @@ async def set_instagram_credentials(
|
|
|
1713
1705
|
lines.append(f"INSTAGRAM_USERNAME={username_to_use}\n")
|
|
1714
1706
|
username_found = True
|
|
1715
1707
|
elif line.startswith("INSTAGRAM_PASSWORD="):
|
|
1716
|
-
|
|
1708
|
+
if data.password:
|
|
1709
|
+
lines.append(f"INSTAGRAM_PASSWORD={data.password}\n")
|
|
1710
|
+
else:
|
|
1711
|
+
lines.append("INSTAGRAM_PASSWORD=\n")
|
|
1717
1712
|
password_found = True
|
|
1718
1713
|
else:
|
|
1719
1714
|
lines.append(line)
|
|
@@ -1721,7 +1716,10 @@ async def set_instagram_credentials(
|
|
|
1721
1716
|
if not username_found:
|
|
1722
1717
|
lines.append(f"INSTAGRAM_USERNAME={username_to_use}\n")
|
|
1723
1718
|
if not password_found:
|
|
1724
|
-
|
|
1719
|
+
if data.password:
|
|
1720
|
+
lines.append(f"INSTAGRAM_PASSWORD={data.password}\n")
|
|
1721
|
+
else:
|
|
1722
|
+
lines.append("INSTAGRAM_PASSWORD=\n")
|
|
1725
1723
|
|
|
1726
1724
|
with open(api_keys_file, "w") as f:
|
|
1727
1725
|
f.writelines(lines)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"groq_gpt_oss_20b": {
|
|
3
3
|
"key": "groq_gpt_oss_20b",
|
|
4
|
-
"avg_response_s":
|
|
5
|
-
"success_count":
|
|
4
|
+
"avg_response_s": 1.6824798610496439,
|
|
5
|
+
"success_count": 67,
|
|
6
6
|
"fail_count": 1,
|
|
7
7
|
"down_until": null,
|
|
8
|
-
"last_used": "2026-04-
|
|
8
|
+
"last_used": "2026-04-08T07:38:04.817302",
|
|
9
9
|
"last_error": null,
|
|
10
10
|
"base_priority": 0.5
|
|
11
11
|
},
|
|
@@ -323,8 +323,8 @@
|
|
|
323
323
|
"key": "gemini_25_flash_lite_vision",
|
|
324
324
|
"avg_response_s": 6.709401964075168,
|
|
325
325
|
"success_count": 14,
|
|
326
|
-
"fail_count":
|
|
327
|
-
"down_until": "2026-04-
|
|
326
|
+
"fail_count": 14,
|
|
327
|
+
"down_until": "2026-04-08T07:42:57.074461",
|
|
328
328
|
"last_used": "2026-02-24T09:45:09.831171",
|
|
329
329
|
"last_error": "No module named 'google.generativeai'",
|
|
330
330
|
"base_priority": 1.5
|
|
@@ -333,8 +333,8 @@
|
|
|
333
333
|
"key": "gemini_25_pro_vision",
|
|
334
334
|
"avg_response_s": null,
|
|
335
335
|
"success_count": 0,
|
|
336
|
-
"fail_count":
|
|
337
|
-
"down_until": "2026-04-
|
|
336
|
+
"fail_count": 17,
|
|
337
|
+
"down_until": "2026-04-08T07:42:57.067497",
|
|
338
338
|
"last_used": null,
|
|
339
339
|
"last_error": "No module named 'google.generativeai'",
|
|
340
340
|
"base_priority": 2
|
|
@@ -353,8 +353,8 @@
|
|
|
353
353
|
"key": "gemini_3_pro_vision",
|
|
354
354
|
"avg_response_s": null,
|
|
355
355
|
"success_count": 0,
|
|
356
|
-
"fail_count":
|
|
357
|
-
"down_until": "2026-04-
|
|
356
|
+
"fail_count": 16,
|
|
357
|
+
"down_until": "2026-04-08T07:42:57.067497",
|
|
358
358
|
"last_used": null,
|
|
359
359
|
"last_error": "No module named 'google.generativeai'",
|
|
360
360
|
"base_priority": 3
|
|
@@ -363,8 +363,8 @@
|
|
|
363
363
|
"key": "gemini_31_pro_vision",
|
|
364
364
|
"avg_response_s": null,
|
|
365
365
|
"success_count": 0,
|
|
366
|
-
"fail_count":
|
|
367
|
-
"down_until": "2026-04-
|
|
366
|
+
"fail_count": 15,
|
|
367
|
+
"down_until": "2026-04-08T07:42:57.074461",
|
|
368
368
|
"last_used": null,
|
|
369
369
|
"last_error": "No module named 'google.generativeai'",
|
|
370
370
|
"base_priority": 3.5
|
|
@@ -373,8 +373,8 @@
|
|
|
373
373
|
"key": "gemini_20_flash_vision",
|
|
374
374
|
"avg_response_s": null,
|
|
375
375
|
"success_count": 0,
|
|
376
|
-
"fail_count":
|
|
377
|
-
"down_until": "2026-04-
|
|
376
|
+
"fail_count": 14,
|
|
377
|
+
"down_until": "2026-04-08T07:42:57.077651",
|
|
378
378
|
"last_used": null,
|
|
379
379
|
"last_error": "No module named 'google.generativeai'",
|
|
380
380
|
"base_priority": 4
|
|
@@ -383,8 +383,8 @@
|
|
|
383
383
|
"key": "gemini_20_flash_lite_vision",
|
|
384
384
|
"avg_response_s": null,
|
|
385
385
|
"success_count": 0,
|
|
386
|
-
"fail_count":
|
|
387
|
-
"down_until": "2026-04-
|
|
386
|
+
"fail_count": 14,
|
|
387
|
+
"down_until": "2026-04-08T07:42:57.077651",
|
|
388
388
|
"last_used": null,
|
|
389
389
|
"last_error": "No module named 'google.generativeai'",
|
|
390
390
|
"base_priority": 4.5
|
|
@@ -393,19 +393,19 @@
|
|
|
393
393
|
"key": "gemini_15_flash_vision",
|
|
394
394
|
"avg_response_s": null,
|
|
395
395
|
"success_count": 0,
|
|
396
|
-
"fail_count":
|
|
397
|
-
"down_until": "2026-04-
|
|
396
|
+
"fail_count": 14,
|
|
397
|
+
"down_until": "2026-04-08T07:42:57.079755",
|
|
398
398
|
"last_used": null,
|
|
399
399
|
"last_error": "No module named 'google.generativeai'",
|
|
400
400
|
"base_priority": 4.8
|
|
401
401
|
},
|
|
402
402
|
"groq_llama4_scout_vision": {
|
|
403
403
|
"key": "groq_llama4_scout_vision",
|
|
404
|
-
"avg_response_s": 1.
|
|
405
|
-
"success_count":
|
|
404
|
+
"avg_response_s": 1.2924692601270893,
|
|
405
|
+
"success_count": 42,
|
|
406
406
|
"fail_count": 0,
|
|
407
407
|
"down_until": null,
|
|
408
|
-
"last_used": "2026-04-
|
|
408
|
+
"last_used": "2026-04-08T07:37:59.761209",
|
|
409
409
|
"last_error": null,
|
|
410
410
|
"base_priority": 5
|
|
411
411
|
},
|
|
@@ -489,8 +489,8 @@
|
|
|
489
489
|
"last_error": "No module named 'ollama'",
|
|
490
490
|
"base_priority": 100
|
|
491
491
|
},
|
|
492
|
-
"
|
|
493
|
-
"key": "
|
|
492
|
+
"dyn_google_lyria-3-pro-preview": {
|
|
493
|
+
"key": "dyn_google_lyria-3-pro-preview",
|
|
494
494
|
"avg_response_s": null,
|
|
495
495
|
"success_count": 0,
|
|
496
496
|
"fail_count": 0,
|
|
@@ -499,8 +499,8 @@
|
|
|
499
499
|
"last_error": null,
|
|
500
500
|
"base_priority": 20
|
|
501
501
|
},
|
|
502
|
-
"
|
|
503
|
-
"key": "
|
|
502
|
+
"dyn_v_google_lyria-3-pro-preview": {
|
|
503
|
+
"key": "dyn_v_google_lyria-3-pro-preview",
|
|
504
504
|
"avg_response_s": null,
|
|
505
505
|
"success_count": 0,
|
|
506
506
|
"fail_count": 0,
|
|
@@ -509,8 +509,8 @@
|
|
|
509
509
|
"last_error": null,
|
|
510
510
|
"base_priority": 20
|
|
511
511
|
},
|
|
512
|
-
"dyn_google_lyria-3-
|
|
513
|
-
"key": "dyn_google_lyria-3-
|
|
512
|
+
"dyn_google_lyria-3-clip-preview": {
|
|
513
|
+
"key": "dyn_google_lyria-3-clip-preview",
|
|
514
514
|
"avg_response_s": null,
|
|
515
515
|
"success_count": 0,
|
|
516
516
|
"fail_count": 0,
|
|
@@ -519,8 +519,8 @@
|
|
|
519
519
|
"last_error": null,
|
|
520
520
|
"base_priority": 21
|
|
521
521
|
},
|
|
522
|
-
"dyn_v_google_lyria-3-
|
|
523
|
-
"key": "dyn_v_google_lyria-3-
|
|
522
|
+
"dyn_v_google_lyria-3-clip-preview": {
|
|
523
|
+
"key": "dyn_v_google_lyria-3-clip-preview",
|
|
524
524
|
"avg_response_s": null,
|
|
525
525
|
"success_count": 0,
|
|
526
526
|
"fail_count": 0,
|
|
@@ -529,8 +529,8 @@
|
|
|
529
529
|
"last_error": null,
|
|
530
530
|
"base_priority": 21
|
|
531
531
|
},
|
|
532
|
-
"
|
|
533
|
-
"key": "
|
|
532
|
+
"dyn_google_gemma-4-26b-a4b-it_free": {
|
|
533
|
+
"key": "dyn_google_gemma-4-26b-a4b-it_free",
|
|
534
534
|
"avg_response_s": null,
|
|
535
535
|
"success_count": 0,
|
|
536
536
|
"fail_count": 0,
|
|
@@ -539,8 +539,8 @@
|
|
|
539
539
|
"last_error": null,
|
|
540
540
|
"base_priority": 22
|
|
541
541
|
},
|
|
542
|
-
"
|
|
543
|
-
"key": "
|
|
542
|
+
"dyn_v_google_gemma-4-26b-a4b-it_free": {
|
|
543
|
+
"key": "dyn_v_google_gemma-4-26b-a4b-it_free",
|
|
544
544
|
"avg_response_s": null,
|
|
545
545
|
"success_count": 0,
|
|
546
546
|
"fail_count": 0,
|
|
@@ -549,6 +549,26 @@
|
|
|
549
549
|
"last_error": null,
|
|
550
550
|
"base_priority": 22
|
|
551
551
|
},
|
|
552
|
+
"dyn_google_gemma-4-31b-it_free": {
|
|
553
|
+
"key": "dyn_google_gemma-4-31b-it_free",
|
|
554
|
+
"avg_response_s": null,
|
|
555
|
+
"success_count": 0,
|
|
556
|
+
"fail_count": 0,
|
|
557
|
+
"down_until": null,
|
|
558
|
+
"last_used": null,
|
|
559
|
+
"last_error": null,
|
|
560
|
+
"base_priority": 23
|
|
561
|
+
},
|
|
562
|
+
"dyn_v_google_gemma-4-31b-it_free": {
|
|
563
|
+
"key": "dyn_v_google_gemma-4-31b-it_free",
|
|
564
|
+
"avg_response_s": null,
|
|
565
|
+
"success_count": 0,
|
|
566
|
+
"fail_count": 0,
|
|
567
|
+
"down_until": null,
|
|
568
|
+
"last_used": null,
|
|
569
|
+
"last_error": null,
|
|
570
|
+
"base_priority": 23
|
|
571
|
+
},
|
|
552
572
|
"dyn_nvidia_nemotron-3-super-120b-a12b_free": {
|
|
553
573
|
"key": "dyn_nvidia_nemotron-3-super-120b-a12b_free",
|
|
554
574
|
"avg_response_s": null,
|
|
@@ -557,7 +577,7 @@
|
|
|
557
577
|
"down_until": null,
|
|
558
578
|
"last_used": null,
|
|
559
579
|
"last_error": null,
|
|
560
|
-
"base_priority":
|
|
580
|
+
"base_priority": 24
|
|
561
581
|
},
|
|
562
582
|
"dyn_openrouter_free": {
|
|
563
583
|
"key": "dyn_openrouter_free",
|
|
@@ -567,7 +587,7 @@
|
|
|
567
587
|
"down_until": null,
|
|
568
588
|
"last_used": null,
|
|
569
589
|
"last_error": null,
|
|
570
|
-
"base_priority":
|
|
590
|
+
"base_priority": 25
|
|
571
591
|
},
|
|
572
592
|
"dyn_v_openrouter_free": {
|
|
573
593
|
"key": "dyn_v_openrouter_free",
|
|
@@ -577,7 +597,7 @@
|
|
|
577
597
|
"down_until": null,
|
|
578
598
|
"last_used": null,
|
|
579
599
|
"last_error": null,
|
|
580
|
-
"base_priority":
|
|
600
|
+
"base_priority": 25
|
|
581
601
|
},
|
|
582
602
|
"dyn_arcee-ai_trinity-mini_free": {
|
|
583
603
|
"key": "dyn_arcee-ai_trinity-mini_free",
|
|
@@ -587,7 +607,7 @@
|
|
|
587
607
|
"down_until": null,
|
|
588
608
|
"last_used": null,
|
|
589
609
|
"last_error": null,
|
|
590
|
-
"base_priority":
|
|
610
|
+
"base_priority": 29
|
|
591
611
|
},
|
|
592
612
|
"dyn_nvidia_nemotron-nano-9b-v2_free": {
|
|
593
613
|
"key": "dyn_nvidia_nemotron-nano-9b-v2_free",
|
|
@@ -597,7 +617,7 @@
|
|
|
597
617
|
"down_until": null,
|
|
598
618
|
"last_used": null,
|
|
599
619
|
"last_error": null,
|
|
600
|
-
"base_priority":
|
|
620
|
+
"base_priority": 30
|
|
601
621
|
},
|
|
602
622
|
"dyn_minimax_minimax-m2_5_free": {
|
|
603
623
|
"key": "dyn_minimax_minimax-m2_5_free",
|
|
@@ -607,7 +627,7 @@
|
|
|
607
627
|
"down_until": null,
|
|
608
628
|
"last_used": null,
|
|
609
629
|
"last_error": null,
|
|
610
|
-
"base_priority":
|
|
630
|
+
"base_priority": 31
|
|
611
631
|
},
|
|
612
632
|
"dyn_qwen_qwen3-coder_free": {
|
|
613
633
|
"key": "dyn_qwen_qwen3-coder_free",
|
|
@@ -617,7 +637,7 @@
|
|
|
617
637
|
"down_until": null,
|
|
618
638
|
"last_used": null,
|
|
619
639
|
"last_error": null,
|
|
620
|
-
"base_priority":
|
|
640
|
+
"base_priority": 32
|
|
621
641
|
},
|
|
622
642
|
"dyn_liquid_lfm-2_5-1_2b-thinking_free": {
|
|
623
643
|
"key": "dyn_liquid_lfm-2_5-1_2b-thinking_free",
|
|
@@ -627,7 +647,7 @@
|
|
|
627
647
|
"down_until": null,
|
|
628
648
|
"last_used": null,
|
|
629
649
|
"last_error": null,
|
|
630
|
-
"base_priority":
|
|
650
|
+
"base_priority": 33
|
|
631
651
|
},
|
|
632
652
|
"dyn_liquid_lfm-2_5-1_2b-instruct_free": {
|
|
633
653
|
"key": "dyn_liquid_lfm-2_5-1_2b-instruct_free",
|
|
@@ -637,7 +657,7 @@
|
|
|
637
657
|
"down_until": null,
|
|
638
658
|
"last_used": null,
|
|
639
659
|
"last_error": null,
|
|
640
|
-
"base_priority":
|
|
660
|
+
"base_priority": 34
|
|
641
661
|
},
|
|
642
662
|
"dyn_arcee-ai_trinity-large-preview_free": {
|
|
643
663
|
"key": "dyn_arcee-ai_trinity-large-preview_free",
|
|
@@ -647,7 +667,7 @@
|
|
|
647
667
|
"down_until": null,
|
|
648
668
|
"last_used": null,
|
|
649
669
|
"last_error": null,
|
|
650
|
-
"base_priority":
|
|
670
|
+
"base_priority": 36
|
|
651
671
|
},
|
|
652
672
|
"dyn_meta-llama_llama-3_2-3b-instruct_free": {
|
|
653
673
|
"key": "dyn_meta-llama_llama-3_2-3b-instruct_free",
|
|
@@ -657,7 +677,7 @@
|
|
|
657
677
|
"down_until": null,
|
|
658
678
|
"last_used": null,
|
|
659
679
|
"last_error": null,
|
|
660
|
-
"base_priority":
|
|
680
|
+
"base_priority": 40
|
|
661
681
|
},
|
|
662
682
|
"dyn_google_gemma-3n-e2b-it_free": {
|
|
663
683
|
"key": "dyn_google_gemma-3n-e2b-it_free",
|
|
@@ -667,7 +687,7 @@
|
|
|
667
687
|
"down_until": null,
|
|
668
688
|
"last_used": null,
|
|
669
689
|
"last_error": null,
|
|
670
|
-
"base_priority":
|
|
690
|
+
"base_priority": 44
|
|
671
691
|
},
|
|
672
692
|
"dyn_google_gemma-3-4b-it_free": {
|
|
673
693
|
"key": "dyn_google_gemma-3-4b-it_free",
|
|
@@ -677,7 +697,7 @@
|
|
|
677
697
|
"down_until": null,
|
|
678
698
|
"last_used": null,
|
|
679
699
|
"last_error": null,
|
|
680
|
-
"base_priority":
|
|
700
|
+
"base_priority": 45
|
|
681
701
|
},
|
|
682
702
|
"dyn_v_google_gemma-3-4b-it_free": {
|
|
683
703
|
"key": "dyn_v_google_gemma-3-4b-it_free",
|
|
@@ -687,7 +707,7 @@
|
|
|
687
707
|
"down_until": null,
|
|
688
708
|
"last_used": null,
|
|
689
709
|
"last_error": null,
|
|
690
|
-
"base_priority":
|
|
710
|
+
"base_priority": 45
|
|
691
711
|
},
|
|
692
712
|
"dyn_google_gemma-3n-e4b-it_free": {
|
|
693
713
|
"key": "dyn_google_gemma-3n-e4b-it_free",
|
|
@@ -697,7 +717,7 @@
|
|
|
697
717
|
"down_until": null,
|
|
698
718
|
"last_used": null,
|
|
699
719
|
"last_error": null,
|
|
700
|
-
"base_priority":
|
|
720
|
+
"base_priority": 46
|
|
701
721
|
},
|
|
702
722
|
"dyn_google_gemma-3-12b-it_free": {
|
|
703
723
|
"key": "dyn_google_gemma-3-12b-it_free",
|
|
@@ -707,7 +727,7 @@
|
|
|
707
727
|
"down_until": null,
|
|
708
728
|
"last_used": null,
|
|
709
729
|
"last_error": null,
|
|
710
|
-
"base_priority":
|
|
730
|
+
"base_priority": 47
|
|
711
731
|
},
|
|
712
732
|
"dyn_v_google_gemma-3-12b-it_free": {
|
|
713
733
|
"key": "dyn_v_google_gemma-3-12b-it_free",
|
|
@@ -717,6 +737,6 @@
|
|
|
717
737
|
"down_until": null,
|
|
718
738
|
"last_used": null,
|
|
719
739
|
"last_error": null,
|
|
720
|
-
"base_priority":
|
|
740
|
+
"base_priority": 47
|
|
721
741
|
}
|
|
722
742
|
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cached_at": "2026-04-
|
|
2
|
+
"cached_at": "2026-04-08T06:42:57.628411",
|
|
3
3
|
"models": [
|
|
4
4
|
{
|
|
5
|
-
"id": "
|
|
6
|
-
"canonical_slug": "
|
|
7
|
-
"hugging_face_id":
|
|
8
|
-
"name": "
|
|
9
|
-
"created":
|
|
10
|
-
"description": "
|
|
11
|
-
"context_length":
|
|
5
|
+
"id": "google/lyria-3-pro-preview",
|
|
6
|
+
"canonical_slug": "google/lyria-3-pro-preview-20260330",
|
|
7
|
+
"hugging_face_id": null,
|
|
8
|
+
"name": "Google: Lyria 3 Pro Preview",
|
|
9
|
+
"created": 1774907286,
|
|
10
|
+
"description": "Full-length songs are priced at $0.08 per song. Lyria 3 is Google's family of music generation models, available through the Gemini API. With Lyria 3, you can generate high-quality, 48kHz...",
|
|
11
|
+
"context_length": 1048576,
|
|
12
12
|
"architecture": {
|
|
13
|
-
"modality": "text+image
|
|
13
|
+
"modality": "text+image->text+audio",
|
|
14
14
|
"input_modalities": [
|
|
15
15
|
"text",
|
|
16
|
-
"image"
|
|
17
|
-
"video"
|
|
16
|
+
"image"
|
|
18
17
|
],
|
|
19
18
|
"output_modalities": [
|
|
20
|
-
"text"
|
|
19
|
+
"text",
|
|
20
|
+
"audio"
|
|
21
21
|
],
|
|
22
|
-
"tokenizer": "
|
|
22
|
+
"tokenizer": "Other",
|
|
23
23
|
"instruct_type": null
|
|
24
24
|
},
|
|
25
25
|
"pricing": {
|
|
@@ -27,22 +27,16 @@
|
|
|
27
27
|
"completion": "0"
|
|
28
28
|
},
|
|
29
29
|
"top_provider": {
|
|
30
|
-
"context_length":
|
|
30
|
+
"context_length": 1048576,
|
|
31
31
|
"max_completion_tokens": 65536,
|
|
32
32
|
"is_moderated": false
|
|
33
33
|
},
|
|
34
34
|
"per_request_limits": null,
|
|
35
35
|
"supported_parameters": [
|
|
36
|
-
"include_reasoning",
|
|
37
36
|
"max_tokens",
|
|
38
|
-
"presence_penalty",
|
|
39
|
-
"reasoning",
|
|
40
37
|
"response_format",
|
|
41
38
|
"seed",
|
|
42
|
-
"structured_outputs",
|
|
43
39
|
"temperature",
|
|
44
|
-
"tool_choice",
|
|
45
|
-
"tools",
|
|
46
40
|
"top_p"
|
|
47
41
|
],
|
|
48
42
|
"default_parameters": {
|
|
@@ -54,18 +48,18 @@
|
|
|
54
48
|
"repetition_penalty": null
|
|
55
49
|
},
|
|
56
50
|
"knowledge_cutoff": null,
|
|
57
|
-
"expiration_date":
|
|
51
|
+
"expiration_date": null,
|
|
58
52
|
"links": {
|
|
59
|
-
"details": "/api/v1/models/
|
|
53
|
+
"details": "/api/v1/models/google/lyria-3-pro-preview-20260330/endpoints"
|
|
60
54
|
}
|
|
61
55
|
},
|
|
62
56
|
{
|
|
63
|
-
"id": "google/lyria-3-
|
|
64
|
-
"canonical_slug": "google/lyria-3-
|
|
57
|
+
"id": "google/lyria-3-clip-preview",
|
|
58
|
+
"canonical_slug": "google/lyria-3-clip-preview-20260330",
|
|
65
59
|
"hugging_face_id": null,
|
|
66
|
-
"name": "Google: Lyria 3
|
|
67
|
-
"created":
|
|
68
|
-
"description": "
|
|
60
|
+
"name": "Google: Lyria 3 Clip Preview",
|
|
61
|
+
"created": 1774907255,
|
|
62
|
+
"description": "30 second duration clips are priced at $0.04 per clip. Lyria 3 is Google's family of music generation models, available through the Gemini API. With Lyria 3, you can generate...",
|
|
69
63
|
"context_length": 1048576,
|
|
70
64
|
"architecture": {
|
|
71
65
|
"modality": "text+image->text+audio",
|
|
@@ -108,28 +102,81 @@
|
|
|
108
102
|
"knowledge_cutoff": null,
|
|
109
103
|
"expiration_date": null,
|
|
110
104
|
"links": {
|
|
111
|
-
"details": "/api/v1/models/google/lyria-3-
|
|
105
|
+
"details": "/api/v1/models/google/lyria-3-clip-preview-20260330/endpoints"
|
|
112
106
|
}
|
|
113
107
|
},
|
|
114
108
|
{
|
|
115
|
-
"id": "google/
|
|
116
|
-
"canonical_slug": "google/
|
|
117
|
-
"hugging_face_id":
|
|
118
|
-
"name": "Google:
|
|
119
|
-
"created":
|
|
120
|
-
"description": "
|
|
121
|
-
"context_length":
|
|
109
|
+
"id": "google/gemma-4-26b-a4b-it:free",
|
|
110
|
+
"canonical_slug": "google/gemma-4-26b-a4b-it-20260403",
|
|
111
|
+
"hugging_face_id": "google/gemma-4-26B-A4B-it",
|
|
112
|
+
"name": "Google: Gemma 4 26B A4B (free)",
|
|
113
|
+
"created": 1775227989,
|
|
114
|
+
"description": "Gemma 4 26B A4B IT is an instruction-tuned Mixture-of-Experts (MoE) model from Google DeepMind. Despite 25.2B total parameters, only 3.8B activate per token during inference \u2014 delivering near-31B quality at...",
|
|
115
|
+
"context_length": 262144,
|
|
122
116
|
"architecture": {
|
|
123
|
-
"modality": "text+image->text
|
|
117
|
+
"modality": "text+image+video->text",
|
|
124
118
|
"input_modalities": [
|
|
119
|
+
"image",
|
|
125
120
|
"text",
|
|
126
|
-
"
|
|
121
|
+
"video"
|
|
127
122
|
],
|
|
128
123
|
"output_modalities": [
|
|
124
|
+
"text"
|
|
125
|
+
],
|
|
126
|
+
"tokenizer": "Gemma",
|
|
127
|
+
"instruct_type": null
|
|
128
|
+
},
|
|
129
|
+
"pricing": {
|
|
130
|
+
"prompt": "0",
|
|
131
|
+
"completion": "0"
|
|
132
|
+
},
|
|
133
|
+
"top_provider": {
|
|
134
|
+
"context_length": 262144,
|
|
135
|
+
"max_completion_tokens": 32768,
|
|
136
|
+
"is_moderated": false
|
|
137
|
+
},
|
|
138
|
+
"per_request_limits": null,
|
|
139
|
+
"supported_parameters": [
|
|
140
|
+
"include_reasoning",
|
|
141
|
+
"max_tokens",
|
|
142
|
+
"reasoning",
|
|
143
|
+
"response_format",
|
|
144
|
+
"seed",
|
|
145
|
+
"temperature",
|
|
146
|
+
"tool_choice",
|
|
147
|
+
"tools",
|
|
148
|
+
"top_p"
|
|
149
|
+
],
|
|
150
|
+
"default_parameters": {
|
|
151
|
+
"temperature": 1,
|
|
152
|
+
"top_p": 0.95,
|
|
153
|
+
"top_k": 64
|
|
154
|
+
},
|
|
155
|
+
"knowledge_cutoff": null,
|
|
156
|
+
"expiration_date": null,
|
|
157
|
+
"links": {
|
|
158
|
+
"details": "/api/v1/models/google/gemma-4-26b-a4b-it-20260403/endpoints"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"id": "google/gemma-4-31b-it:free",
|
|
163
|
+
"canonical_slug": "google/gemma-4-31b-it-20260402",
|
|
164
|
+
"hugging_face_id": "google/gemma-4-31B-it",
|
|
165
|
+
"name": "Google: Gemma 4 31B (free)",
|
|
166
|
+
"created": 1775148486,
|
|
167
|
+
"description": "Gemma 4 31B Instruct is Google DeepMind's 30.7B dense multimodal model supporting text and image input with text output. Features a 256K token context window, configurable thinking/reasoning mode, native function...",
|
|
168
|
+
"context_length": 262144,
|
|
169
|
+
"architecture": {
|
|
170
|
+
"modality": "text+image+video->text",
|
|
171
|
+
"input_modalities": [
|
|
172
|
+
"image",
|
|
129
173
|
"text",
|
|
130
|
-
"
|
|
174
|
+
"video"
|
|
131
175
|
],
|
|
132
|
-
"
|
|
176
|
+
"output_modalities": [
|
|
177
|
+
"text"
|
|
178
|
+
],
|
|
179
|
+
"tokenizer": "Gemma",
|
|
133
180
|
"instruct_type": null
|
|
134
181
|
},
|
|
135
182
|
"pricing": {
|
|
@@ -137,22 +184,26 @@
|
|
|
137
184
|
"completion": "0"
|
|
138
185
|
},
|
|
139
186
|
"top_provider": {
|
|
140
|
-
"context_length":
|
|
141
|
-
"max_completion_tokens":
|
|
187
|
+
"context_length": 262144,
|
|
188
|
+
"max_completion_tokens": 32768,
|
|
142
189
|
"is_moderated": false
|
|
143
190
|
},
|
|
144
191
|
"per_request_limits": null,
|
|
145
192
|
"supported_parameters": [
|
|
193
|
+
"include_reasoning",
|
|
146
194
|
"max_tokens",
|
|
195
|
+
"reasoning",
|
|
147
196
|
"response_format",
|
|
148
197
|
"seed",
|
|
149
198
|
"temperature",
|
|
199
|
+
"tool_choice",
|
|
200
|
+
"tools",
|
|
150
201
|
"top_p"
|
|
151
202
|
],
|
|
152
203
|
"default_parameters": {
|
|
153
|
-
"temperature":
|
|
154
|
-
"top_p":
|
|
155
|
-
"top_k":
|
|
204
|
+
"temperature": 1,
|
|
205
|
+
"top_p": 0.95,
|
|
206
|
+
"top_k": 64,
|
|
156
207
|
"frequency_penalty": null,
|
|
157
208
|
"presence_penalty": null,
|
|
158
209
|
"repetition_penalty": null
|
|
@@ -160,7 +211,7 @@
|
|
|
160
211
|
"knowledge_cutoff": null,
|
|
161
212
|
"expiration_date": null,
|
|
162
213
|
"links": {
|
|
163
|
-
"details": "/api/v1/models/google/
|
|
214
|
+
"details": "/api/v1/models/google/gemma-4-31b-it-20260402/endpoints"
|
|
164
215
|
}
|
|
165
216
|
},
|
|
166
217
|
{
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import instaloader
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
sessionid = "49367419455%3AxScylb4HDGaCmD%3A13%3AAYiHyY8zbdE5_JHlwzFO1Wpm70kmtCJaNQ1WJg35uQ"
|
|
5
|
+
|
|
6
|
+
L = instaloader.Instaloader(
|
|
7
|
+
dirname_pattern="test_download_{shortcode}",
|
|
8
|
+
download_video_thumbnails=False,
|
|
9
|
+
download_geotags=False,
|
|
10
|
+
download_comments=False,
|
|
11
|
+
save_metadata=False,
|
|
12
|
+
compress_json=False
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Inject session
|
|
16
|
+
L.context._session.cookies.set("sessionid", sessionid, domain=".instagram.com")
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
shortcode = 'DW13xFQkvMR' # Meta/Instagram demo reel
|
|
20
|
+
print(f"Downloading post/reel {shortcode}...")
|
|
21
|
+
post = instaloader.Post.from_shortcode(L.context, shortcode)
|
|
22
|
+
L.download_post(post, target=f"test_download_{shortcode}")
|
|
23
|
+
print(f"\nDownload completed!")
|
|
24
|
+
|
|
25
|
+
download_dir = f"test_download_{shortcode}"
|
|
26
|
+
if os.path.exists(download_dir):
|
|
27
|
+
files = os.listdir(download_dir)
|
|
28
|
+
print("Downloaded files:")
|
|
29
|
+
for f in files:
|
|
30
|
+
print(f" - {f}")
|
|
31
|
+
except Exception as e:
|
|
32
|
+
print(f"Failed: {e}")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import instaloader
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
logging.basicConfig(level=logging.INFO)
|
|
5
|
+
sessionid = "49367419455%3AxScylb4HDGaCmD%3A13%3AAYiHyY8zbdE5_JHlwzFO1Wpm70kmtCJaNQ1WJg35uQ"
|
|
6
|
+
|
|
7
|
+
L = instaloader.Instaloader()
|
|
8
|
+
L.context._session.cookies.set("sessionid", sessionid, domain=".instagram.com")
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
print("Testing profile fetch...")
|
|
12
|
+
profile = instaloader.Profile.from_username(L.context, 'zuck')
|
|
13
|
+
print(f"Success! Found profile: {profile.full_name} ({profile.followers} followers)")
|
|
14
|
+
|
|
15
|
+
print("Testing post download...")
|
|
16
|
+
post = instaloader.Post.from_shortcode(L.context, 'DW13xFQkvMR')
|
|
17
|
+
print(f"Success! Found post by: {post.owner_username}, Caption: {str(post.caption)[:30]}...")
|
|
18
|
+
|
|
19
|
+
print("Session ID works perfectly.")
|
|
20
|
+
except Exception as e:
|
|
21
|
+
print(f"Failed: {e}")
|
package/payload/test_backend.py
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
SuperBrain - Comprehensive Backend Test Suite
|
|
3
|
-
Tests all API endpoints against the running backend.
|
|
4
|
-
"""
|
|
5
|
-
import requests
|
|
6
|
-
import sys
|
|
7
|
-
import json
|
|
8
|
-
import time
|
|
9
|
-
|
|
10
|
-
# Config
|
|
11
|
-
BASE_URL = "http://localhost:5000"
|
|
12
|
-
TOKEN = "4XVTLWWV"
|
|
13
|
-
HEADERS = {"X-API-Key": TOKEN, "Content-Type": "application/json"}
|
|
14
|
-
|
|
15
|
-
TEST_YT_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
|
16
|
-
|
|
17
|
-
passed = 0
|
|
18
|
-
failed = 0
|
|
19
|
-
skipped = 0
|
|
20
|
-
|
|
21
|
-
def test(name, condition, detail=""):
|
|
22
|
-
global passed, failed
|
|
23
|
-
if condition:
|
|
24
|
-
passed += 1
|
|
25
|
-
print(f" [PASS] {name}")
|
|
26
|
-
else:
|
|
27
|
-
failed += 1
|
|
28
|
-
print(f" [FAIL] {name} -- {detail}")
|
|
29
|
-
|
|
30
|
-
def skip(name, reason):
|
|
31
|
-
global skipped
|
|
32
|
-
skipped += 1
|
|
33
|
-
print(f" [SKIP] {name} -- {reason}")
|
|
34
|
-
|
|
35
|
-
# Phase 1: Health & Auth
|
|
36
|
-
print("\n--- Phase 1: Health & Auth ---")
|
|
37
|
-
|
|
38
|
-
try:
|
|
39
|
-
r = requests.get(f"{BASE_URL}/status", timeout=5)
|
|
40
|
-
test("1.1 GET /status", r.status_code == 200 and r.json().get("status") == "online", f"status={r.status_code}")
|
|
41
|
-
except Exception as e:
|
|
42
|
-
test("1.1 GET /status", False, str(e))
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
r = requests.get(f"{BASE_URL}/health", headers=HEADERS, timeout=5)
|
|
46
|
-
data = r.json()
|
|
47
|
-
test("1.2 GET /health (auth)", r.status_code == 200 and "database" in data, f"status={r.status_code}")
|
|
48
|
-
except Exception as e:
|
|
49
|
-
test("1.2 GET /health", False, str(e))
|
|
50
|
-
|
|
51
|
-
try:
|
|
52
|
-
r = requests.get(f"{BASE_URL}/health", headers={"X-API-Key": "WRONGTKN"}, timeout=5)
|
|
53
|
-
test("1.3 Auth rejection (wrong)", r.status_code == 401, f"status={r.status_code}")
|
|
54
|
-
except Exception as e:
|
|
55
|
-
test("1.3 Auth rejection", False, str(e))
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
r = requests.get(f"{BASE_URL}/health", timeout=5)
|
|
59
|
-
test("1.4 Auth rejection (none)", r.status_code in [401, 403, 422], f"status={r.status_code}")
|
|
60
|
-
except Exception as e:
|
|
61
|
-
test("1.4 Auth rejection (none)", False, str(e))
|
|
62
|
-
|
|
63
|
-
# Phase 2: Read Endpoints
|
|
64
|
-
print("\n--- Phase 2: Read Endpoints ---")
|
|
65
|
-
|
|
66
|
-
try:
|
|
67
|
-
r = requests.get(f"{BASE_URL}/recent?limit=10", headers=HEADERS, timeout=10)
|
|
68
|
-
data = r.json()
|
|
69
|
-
test("2.1 GET /recent", r.status_code == 200 and "data" in data, f"status={r.status_code}")
|
|
70
|
-
print(f" found {len(data.get('data', []))} posts")
|
|
71
|
-
except Exception as e:
|
|
72
|
-
test("2.1 GET /recent", False, str(e))
|
|
73
|
-
|
|
74
|
-
try:
|
|
75
|
-
r = requests.get(f"{BASE_URL}/categories", headers=HEADERS, timeout=10)
|
|
76
|
-
data = r.json()
|
|
77
|
-
test("2.2 GET /categories", r.status_code == 200 and "categories" in data, f"status={r.status_code}")
|
|
78
|
-
print(f" found {len(data.get('categories', []))} categories")
|
|
79
|
-
except Exception as e:
|
|
80
|
-
test("2.2 GET /categories", False, str(e))
|
|
81
|
-
|
|
82
|
-
try:
|
|
83
|
-
r = requests.get(f"{BASE_URL}/queue-status", headers=HEADERS, timeout=10)
|
|
84
|
-
data = r.json()
|
|
85
|
-
test("2.3 GET /queue-status", r.status_code == 200 and "processing_count" in data, f"keys={list(data.keys())}")
|
|
86
|
-
print(f" processing={data.get('processing_count',0)}, queue={data.get('queue_count',0)}, retry={data.get('retry_count',0)}")
|
|
87
|
-
except Exception as e:
|
|
88
|
-
test("2.3 GET /queue-status", False, str(e))
|
|
89
|
-
|
|
90
|
-
try:
|
|
91
|
-
r = requests.get(f"{BASE_URL}/stats", headers=HEADERS, timeout=10)
|
|
92
|
-
test("2.4 GET /stats", r.status_code == 200, f"status={r.status_code}")
|
|
93
|
-
except Exception as e:
|
|
94
|
-
test("2.4 GET /stats", False, str(e))
|
|
95
|
-
|
|
96
|
-
try:
|
|
97
|
-
r = requests.get(f"{BASE_URL}/search?tags=travel&limit=5", headers=HEADERS, timeout=10)
|
|
98
|
-
test("2.5 GET /search?tags=travel", r.status_code == 200, f"status={r.status_code}")
|
|
99
|
-
except Exception as e:
|
|
100
|
-
test("2.5 GET /search", False, str(e))
|
|
101
|
-
|
|
102
|
-
try:
|
|
103
|
-
r = requests.get(f"{BASE_URL}/queue/retry", headers=HEADERS, timeout=10)
|
|
104
|
-
test("2.6 GET /queue/retry", r.status_code == 200, f"status={r.status_code}")
|
|
105
|
-
except Exception as e:
|
|
106
|
-
test("2.6 GET /queue/retry", False, str(e))
|
|
107
|
-
|
|
108
|
-
# Phase 3: Collections CRUD
|
|
109
|
-
print("\n--- Phase 3: Collections CRUD ---")
|
|
110
|
-
|
|
111
|
-
test_collection_id = None
|
|
112
|
-
|
|
113
|
-
try:
|
|
114
|
-
r = requests.get(f"{BASE_URL}/collections", headers=HEADERS, timeout=10)
|
|
115
|
-
data = r.json()
|
|
116
|
-
test("3.1 GET /collections", r.status_code == 200, f"status={r.status_code}")
|
|
117
|
-
print(f" found {len(data)} collections")
|
|
118
|
-
except Exception as e:
|
|
119
|
-
test("3.1 GET /collections", False, str(e))
|
|
120
|
-
|
|
121
|
-
try:
|
|
122
|
-
r = requests.post(f"{BASE_URL}/collections", headers=HEADERS, json={"id": "test-id", "name": "Test Collection", "icon": "test"}, timeout=10)
|
|
123
|
-
data = r.json()
|
|
124
|
-
test("3.2 POST /collections (create)", r.status_code == 200 and data.get("id"), f"status={r.status_code}")
|
|
125
|
-
test_collection_id = data.get("id")
|
|
126
|
-
print(f" created ID: {test_collection_id}")
|
|
127
|
-
except Exception as e:
|
|
128
|
-
test("3.2 POST /collections", False, str(e))
|
|
129
|
-
|
|
130
|
-
if test_collection_id:
|
|
131
|
-
try:
|
|
132
|
-
r = requests.post(f"{BASE_URL}/collections", headers=HEADERS, json={"id": test_collection_id, "name": "Updated Test", "icon": "ok"}, timeout=10)
|
|
133
|
-
test("3.3 POST /collections (update)", r.status_code == 200, f"status={r.status_code}")
|
|
134
|
-
except Exception as e:
|
|
135
|
-
test("3.3 POST /collections (update)", False, str(e))
|
|
136
|
-
|
|
137
|
-
try:
|
|
138
|
-
r = requests.delete(f"{BASE_URL}/collections/{test_collection_id}", headers=HEADERS, timeout=10)
|
|
139
|
-
test("3.4 DELETE /collections", r.status_code == 200, f"status={r.status_code}")
|
|
140
|
-
except Exception as e:
|
|
141
|
-
test("3.4 DELETE /collections", False, str(e))
|
|
142
|
-
else:
|
|
143
|
-
skip("3.3 PUT /collections", "no collection created")
|
|
144
|
-
skip("3.4 DELETE /collections", "no collection created")
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
r = requests.get(f"{BASE_URL}/collections", headers=HEADERS, timeout=10)
|
|
148
|
-
data = r.json()
|
|
149
|
-
collections = data.get("data", [])
|
|
150
|
-
wl = next((c for c in collections if c.get("name") == "Watch Later"), None)
|
|
151
|
-
if wl:
|
|
152
|
-
r = requests.delete(f"{BASE_URL}/collections/{wl['id']}", headers=HEADERS, timeout=10)
|
|
153
|
-
test("3.5 Watch Later protection", r.status_code in [400, 403], f"status={r.status_code} (should be blocked)")
|
|
154
|
-
else:
|
|
155
|
-
skip("3.5 Watch Later protection", "no Watch Later found")
|
|
156
|
-
except Exception as e:
|
|
157
|
-
test("3.5 Watch Later protection", False, str(e))
|
|
158
|
-
|
|
159
|
-
# Phase 4: Settings
|
|
160
|
-
print("\n--- Phase 4: Settings ---")
|
|
161
|
-
|
|
162
|
-
try:
|
|
163
|
-
r = requests.get(f"{BASE_URL}/settings/ai-providers", headers=HEADERS, timeout=10)
|
|
164
|
-
test("4.1 GET /settings/ai-providers", r.status_code == 200, f"status={r.status_code}")
|
|
165
|
-
except Exception as e:
|
|
166
|
-
test("4.1 GET /settings/ai-providers", False, str(e))
|
|
167
|
-
|
|
168
|
-
try:
|
|
169
|
-
r = requests.post(f"{BASE_URL}/settings/ai-providers", headers=HEADERS,
|
|
170
|
-
json={"provider": "groq", "api_key": "gsk_test"}, timeout=10)
|
|
171
|
-
test("4.2 POST ai-providers (Groq)", r.status_code == 200, f"status={r.status_code}")
|
|
172
|
-
except Exception as e:
|
|
173
|
-
test("4.2 POST ai-providers", False, str(e))
|
|
174
|
-
|
|
175
|
-
try:
|
|
176
|
-
r = requests.get(f"{BASE_URL}/settings/instagram", headers=HEADERS, timeout=10)
|
|
177
|
-
test("4.3 GET /settings/instagram", r.status_code == 200, f"status={r.status_code}")
|
|
178
|
-
except Exception as e:
|
|
179
|
-
test("4.3 GET /settings/instagram", False, str(e))
|
|
180
|
-
|
|
181
|
-
try:
|
|
182
|
-
r = requests.post(f"{BASE_URL}/settings/instagram", headers=HEADERS,
|
|
183
|
-
json={"username": "testuser", "password": "testpass"}, timeout=10)
|
|
184
|
-
test("4.4 POST /settings/instagram", r.status_code == 200, f"status={r.status_code}")
|
|
185
|
-
except Exception as e:
|
|
186
|
-
test("4.4 POST /settings/instagram", False, str(e))
|
|
187
|
-
|
|
188
|
-
# Phase 5: Export/Import
|
|
189
|
-
print("\n--- Phase 5: Export/Import ---")
|
|
190
|
-
|
|
191
|
-
try:
|
|
192
|
-
r = requests.get(f"{BASE_URL}/export", headers=HEADERS, timeout=15)
|
|
193
|
-
test("5.1 GET /export", r.status_code == 200, f"status={r.status_code}")
|
|
194
|
-
ed = r.json()
|
|
195
|
-
print(f" exported {len(ed.get('posts', []))} posts, {len(ed.get('collections', []))} collections")
|
|
196
|
-
except Exception as e:
|
|
197
|
-
test("5.1 GET /export", False, str(e))
|
|
198
|
-
|
|
199
|
-
try:
|
|
200
|
-
r = requests.post(f"{BASE_URL}/import", headers=HEADERS,
|
|
201
|
-
json={"posts": [], "collections": [], "mode": "merge"}, timeout=15)
|
|
202
|
-
test("5.2 POST /import (merge, empty)", r.status_code == 200, f"status={r.status_code}")
|
|
203
|
-
except Exception as e:
|
|
204
|
-
test("5.2 POST /import", False, str(e))
|
|
205
|
-
|
|
206
|
-
# Phase 6: Analysis
|
|
207
|
-
print("\n--- Phase 6: Analysis (YouTube) ---")
|
|
208
|
-
|
|
209
|
-
try:
|
|
210
|
-
print(" submitting YouTube URL... (timeout 90s)")
|
|
211
|
-
r = requests.post(f"{BASE_URL}/analyze", headers=HEADERS,
|
|
212
|
-
json={"url": TEST_YT_URL}, timeout=90)
|
|
213
|
-
data = r.json()
|
|
214
|
-
if r.status_code == 200:
|
|
215
|
-
test("6.1 POST /analyze (YouTube)", data.get("success") == True, f"success={data.get('success')}")
|
|
216
|
-
if data.get("data"):
|
|
217
|
-
d = data["data"]
|
|
218
|
-
print(f" title: {d.get('title', 'N/A')[:60]}")
|
|
219
|
-
print(f" category: {d.get('category', 'N/A')}")
|
|
220
|
-
print(f" tags: {str(d.get('tags', [])[:5])}")
|
|
221
|
-
print(f" cached: {data.get('cached', False)}")
|
|
222
|
-
elif r.status_code == 202:
|
|
223
|
-
skip("6.1 POST /analyze (YouTube)", "queued for retry (quota)")
|
|
224
|
-
elif r.status_code == 503:
|
|
225
|
-
skip("6.1 POST /analyze (YouTube)", "503 server busy")
|
|
226
|
-
else:
|
|
227
|
-
test("6.1 POST /analyze (YouTube)", False, f"status={r.status_code}")
|
|
228
|
-
except requests.exceptions.Timeout:
|
|
229
|
-
skip("6.1 POST /analyze (YouTube)", "timed out (90s)")
|
|
230
|
-
except Exception as e:
|
|
231
|
-
test("6.1 POST /analyze (YouTube)", False, str(e))
|
|
232
|
-
|
|
233
|
-
# Summary
|
|
234
|
-
print("\n" + "=" * 50)
|
|
235
|
-
print(f" PASSED: {passed}")
|
|
236
|
-
print(f" FAILED: {failed}")
|
|
237
|
-
print(f" SKIPPED: {skipped}")
|
|
238
|
-
print(f" TOTAL: {passed + failed + skipped}")
|
|
239
|
-
print("=" * 50)
|
|
240
|
-
|
|
241
|
-
sys.exit(1 if failed > 0 else 0)
|
|
File without changes
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
import requests
|
|
3
|
-
import json
|
|
4
|
-
|
|
5
|
-
# Read token
|
|
6
|
-
with open('token.txt', 'r') as f:
|
|
7
|
-
token = f.read().strip()
|
|
8
|
-
|
|
9
|
-
# Test the /recent endpoint
|
|
10
|
-
response = requests.get(
|
|
11
|
-
'http://localhost:5000/recent?limit=10',
|
|
12
|
-
headers={'X-API-Key': token}
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
print(f"Status Code: {response.status_code}")
|
|
16
|
-
print(f"\nResponse:")
|
|
17
|
-
print(json.dumps(response.json(), indent=2))
|
package/payload/tests/test_db.py
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
import sys
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
# Ensure backend root is in sys.path
|
|
6
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
7
|
-
|
|
8
|
-
from core.database import get_db
|
|
9
|
-
|
|
10
|
-
db = get_db()
|
|
11
|
-
posts = db.get_recent(5)
|
|
12
|
-
print(f'Found {len(posts)} posts in database')
|
|
13
|
-
print()
|
|
14
|
-
|
|
15
|
-
for p in posts:
|
|
16
|
-
print(f"Shortcode: {p.get('shortcode', 'N/A')}")
|
|
17
|
-
print(f"Title: {p.get('title', 'N/A')}")
|
|
18
|
-
print(f"Username: {p.get('username', 'N/A')}")
|
|
19
|
-
print(f"URL: {p.get('url', 'N/A')}")
|
|
20
|
-
print(f"Category: {p.get('category', 'N/A')}")
|
|
21
|
-
print(f"Analyzed: {p.get('analyzed_at', 'N/A')}")
|
|
22
|
-
print("-" * 60)
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Legacy filename retained for compatibility.
|
|
4
|
-
This script now validates API key authentication behavior.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
|
|
10
|
-
from fastapi.testclient import TestClient
|
|
11
|
-
|
|
12
|
-
# Ensure backend root is in sys.path
|
|
13
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
14
|
-
|
|
15
|
-
from api import app, API_TOKEN, generate_api_token # noqa: E402
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
client = TestClient(app)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_generate_api_token_shape():
|
|
22
|
-
token = generate_api_token()
|
|
23
|
-
assert isinstance(token, str)
|
|
24
|
-
assert len(token) == 8
|
|
25
|
-
assert token.isalnum()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def test_ping_works_without_auth():
|
|
29
|
-
response = client.get('/ping')
|
|
30
|
-
assert response.status_code == 200
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def test_queue_status_rejects_invalid_api_key():
|
|
34
|
-
response = client.get('/queue-status', headers={'X-API-Key': 'INVALID_TOKEN'})
|
|
35
|
-
assert response.status_code == 401
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_queue_status_accepts_valid_api_key():
|
|
39
|
-
response = client.get('/queue-status', headers={'X-API-Key': API_TOKEN})
|
|
40
|
-
assert response.status_code == 200
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_connect_endpoint_is_deprecated():
|
|
44
|
-
response = client.post('/connect', json={'api_key': API_TOKEN})
|
|
45
|
-
assert response.status_code == 410
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if __name__ == '__main__':
|
|
49
|
-
print('Running API key auth tests...')
|
|
50
|
-
test_generate_api_token_shape()
|
|
51
|
-
print('PASS: API token generation shape')
|
|
52
|
-
|
|
53
|
-
test_ping_works_without_auth()
|
|
54
|
-
print('PASS: /ping unauthenticated access')
|
|
55
|
-
|
|
56
|
-
test_queue_status_rejects_invalid_api_key()
|
|
57
|
-
print('PASS: /queue-status rejects invalid API key')
|
|
58
|
-
|
|
59
|
-
test_queue_status_accepts_valid_api_key()
|
|
60
|
-
print('PASS: /queue-status accepts valid API key')
|
|
61
|
-
|
|
62
|
-
test_connect_endpoint_is_deprecated()
|
|
63
|
-
print('PASS: /connect deprecated behavior')
|
|
64
|
-
|
|
65
|
-
print('\nAll API key auth tests passed!')
|