CompactAI commited on
Commit
1a7b68b
·
verified ·
1 Parent(s): 17ef86f

Delete static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +0 -1325
static/index.html DELETED
@@ -1,1325 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>AIFinder - Identify AI Responses</title>
7
- <style>
8
- @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Outfit:wght@300;400;500;600;700&display=swap');
9
-
10
- * {
11
- margin: 0;
12
- padding: 0;
13
- box-sizing: border-box;
14
- }
15
-
16
- :root {
17
- --bg-primary: #0d0d0d;
18
- --bg-secondary: #171717;
19
- --bg-tertiary: #1f1f1f;
20
- --bg-elevated: #262626;
21
- --text-primary: #f5f5f5;
22
- --text-secondary: #a3a3a3;
23
- --text-muted: #737373;
24
- --accent: #e85d04;
25
- --accent-hover: #f48c06;
26
- --accent-muted: #9c4300;
27
- --success: #22c55e;
28
- --success-muted: #166534;
29
- --border: #333333;
30
- --border-light: #404040;
31
- }
32
-
33
- body {
34
- font-family: 'Outfit', -apple-system, sans-serif;
35
- background: var(--bg-primary);
36
- color: var(--text-primary);
37
- min-height: 100vh;
38
- line-height: 1.6;
39
- }
40
-
41
- .container {
42
- max-width: 900px;
43
- margin: 0 auto;
44
- padding: 2rem 1.5rem;
45
- }
46
-
47
- header {
48
- text-align: center;
49
- margin-bottom: 3rem;
50
- padding-top: 1rem;
51
- }
52
-
53
- .logo {
54
- font-size: 2.5rem;
55
- font-weight: 700;
56
- letter-spacing: -0.05em;
57
- margin-bottom: 0.5rem;
58
- }
59
-
60
- .logo span {
61
- color: var(--accent);
62
- }
63
-
64
- .tagline {
65
- color: var(--text-secondary);
66
- font-size: 1rem;
67
- font-weight: 300;
68
- }
69
-
70
- .card {
71
- background: var(--bg-secondary);
72
- border: 1px solid var(--border);
73
- border-radius: 12px;
74
- padding: 1.5rem;
75
- margin-bottom: 1.5rem;
76
- transition: border-color 0.2s ease;
77
- }
78
-
79
- .card:focus-within {
80
- border-color: var(--border-light);
81
- }
82
-
83
- .card-label {
84
- font-size: 0.75rem;
85
- text-transform: uppercase;
86
- letter-spacing: 0.1em;
87
- color: var(--text-muted);
88
- margin-bottom: 0.75rem;
89
- font-weight: 500;
90
- }
91
-
92
- textarea {
93
- width: 100%;
94
- background: var(--bg-tertiary);
95
- border: 1px solid var(--border);
96
- border-radius: 8px;
97
- padding: 1rem;
98
- color: var(--text-primary);
99
- font-family: 'JetBrains Mono', monospace;
100
- font-size: 0.875rem;
101
- resize: vertical;
102
- min-height: 180px;
103
- transition: border-color 0.2s ease;
104
- }
105
-
106
- textarea:focus {
107
- outline: none;
108
- border-color: var(--accent-muted);
109
- }
110
-
111
- textarea::placeholder {
112
- color: var(--text-muted);
113
- }
114
-
115
- .btn {
116
- display: inline-flex;
117
- align-items: center;
118
- justify-content: center;
119
- gap: 0.5rem;
120
- padding: 0.75rem 1.5rem;
121
- border-radius: 8px;
122
- font-family: 'Outfit', sans-serif;
123
- font-size: 0.9rem;
124
- font-weight: 500;
125
- cursor: pointer;
126
- transition: all 0.2s ease;
127
- border: none;
128
- }
129
-
130
- .btn-primary {
131
- background: var(--accent);
132
- color: white;
133
- }
134
-
135
- .btn-primary:hover:not(:disabled) {
136
- background: var(--accent-hover);
137
- }
138
-
139
- .btn-primary:disabled {
140
- opacity: 0.5;
141
- cursor: not-allowed;
142
- }
143
-
144
- .btn-secondary {
145
- background: var(--bg-tertiary);
146
- color: var(--text-primary);
147
- border: 1px solid var(--border);
148
- }
149
-
150
- .btn-secondary:hover:not(:disabled) {
151
- background: var(--bg-elevated);
152
- border-color: var(--border-light);
153
- }
154
-
155
- .btn-group {
156
- display: flex;
157
- gap: 0.75rem;
158
- flex-wrap: wrap;
159
- }
160
-
161
- .results {
162
- display: none;
163
- }
164
-
165
- .results.visible {
166
- display: block;
167
- animation: fadeIn 0.3s ease;
168
- }
169
-
170
- @keyframes fadeIn {
171
- from { opacity: 0; transform: translateY(10px); }
172
- to { opacity: 1; transform: translateY(0); }
173
- }
174
-
175
- .result-main {
176
- display: flex;
177
- align-items: center;
178
- justify-content: space-between;
179
- padding: 1.25rem;
180
- background: var(--bg-tertiary);
181
- border-radius: 8px;
182
- margin-bottom: 1rem;
183
- }
184
-
185
- .result-provider {
186
- font-size: 1.5rem;
187
- font-weight: 600;
188
- }
189
-
190
- .result-confidence {
191
- font-size: 1.25rem;
192
- font-weight: 500;
193
- color: var(--accent);
194
- }
195
-
196
- .result-bar {
197
- height: 8px;
198
- background: var(--bg-elevated);
199
- border-radius: 4px;
200
- margin-bottom: 1rem;
201
- overflow: hidden;
202
- }
203
-
204
- .result-bar-fill {
205
- height: 100%;
206
- background: var(--accent);
207
- border-radius: 4px;
208
- transition: width 0.5s ease;
209
- }
210
-
211
- .result-list {
212
- list-style: none;
213
- }
214
-
215
- .result-item {
216
- display: flex;
217
- align-items: center;
218
- justify-content: space-between;
219
- padding: 0.75rem 0;
220
- border-bottom: 1px solid var(--border);
221
- }
222
-
223
- .result-item:last-child {
224
- border-bottom: none;
225
- }
226
-
227
- .result-name {
228
- font-weight: 500;
229
- }
230
-
231
- .result-percent {
232
- font-family: 'JetBrains Mono', monospace;
233
- color: var(--text-secondary);
234
- font-size: 0.875rem;
235
- }
236
-
237
- .correction {
238
- display: none;
239
- margin-top: 1.5rem;
240
- padding-top: 1.5rem;
241
- border-top: 1px solid var(--border);
242
- }
243
-
244
- .correction.visible {
245
- display: block;
246
- animation: fadeIn 0.3s ease;
247
- }
248
-
249
- .correction-title {
250
- font-size: 0.875rem;
251
- font-weight: 500;
252
- margin-bottom: 0.75rem;
253
- color: var(--text-secondary);
254
- }
255
-
256
- select {
257
- width: 100%;
258
- padding: 0.75rem 1rem;
259
- background: var(--bg-tertiary);
260
- border: 1px solid var(--border);
261
- border-radius: 8px;
262
- color: var(--text-primary);
263
- font-family: 'Outfit', sans-serif;
264
- font-size: 0.9rem;
265
- margin-bottom: 0.75rem;
266
- cursor: pointer;
267
- }
268
-
269
- select:focus {
270
- outline: none;
271
- border-color: var(--accent-muted);
272
- }
273
-
274
- .stats {
275
- display: flex;
276
- gap: 1.5rem;
277
- margin-bottom: 1.5rem;
278
- flex-wrap: wrap;
279
- }
280
-
281
- .stat {
282
- background: var(--bg-secondary);
283
- border: 1px solid var(--border);
284
- border-radius: 8px;
285
- padding: 1rem 1.25rem;
286
- flex: 1;
287
- min-width: 120px;
288
- }
289
-
290
- .stat-value {
291
- font-size: 1.5rem;
292
- font-weight: 600;
293
- color: var(--accent);
294
- }
295
-
296
- .stat-label {
297
- font-size: 0.75rem;
298
- color: var(--text-muted);
299
- text-transform: uppercase;
300
- letter-spacing: 0.05em;
301
- }
302
-
303
- .actions {
304
- display: flex;
305
- gap: 0.75rem;
306
- margin-top: 1rem;
307
- }
308
-
309
- .toast {
310
- position: fixed;
311
- bottom: 2rem;
312
- right: 2rem;
313
- background: var(--bg-elevated);
314
- border: 1px solid var(--border);
315
- border-radius: 8px;
316
- padding: 1rem 1.5rem;
317
- color: var(--text-primary);
318
- font-size: 0.9rem;
319
- opacity: 0;
320
- transform: translateY(20px);
321
- transition: all 0.3s ease;
322
- z-index: 1000;
323
- }
324
-
325
- .toast.visible {
326
- opacity: 1;
327
- transform: translateY(0);
328
- }
329
-
330
- .toast.success {
331
- border-color: var(--success-muted);
332
- }
333
-
334
- .footer {
335
- text-align: center;
336
- margin-top: 3rem;
337
- padding: 1.5rem;
338
- color: var(--text-muted);
339
- font-size: 0.8rem;
340
- }
341
-
342
- .footer a {
343
- color: var(--text-secondary);
344
- text-decoration: none;
345
- }
346
-
347
- .footer a:hover {
348
- color: var(--accent);
349
- }
350
-
351
- .loading {
352
- display: inline-block;
353
- width: 16px;
354
- height: 16px;
355
- border: 2px solid var(--text-muted);
356
- border-top-color: var(--accent);
357
- border-radius: 50%;
358
- animation: spin 0.8s linear infinite;
359
- }
360
-
361
- @keyframes spin {
362
- to { transform: rotate(360deg); }
363
- }
364
-
365
- .status-indicator {
366
- display: inline-flex;
367
- align-items: center;
368
- gap: 0.5rem;
369
- font-size: 0.8rem;
370
- color: var(--text-muted);
371
- margin-bottom: 1rem;
372
- }
373
-
374
- .status-dot {
375
- width: 8px;
376
- height: 8px;
377
- border-radius: 50%;
378
- background: var(--success);
379
- }
380
-
381
- .status-dot.loading {
382
- background: var(--accent);
383
- animation: pulse 1s ease infinite;
384
- }
385
-
386
- @keyframes pulse {
387
- 0%, 100% { opacity: 1; }
388
- 50% { opacity: 0.5; }
389
- }
390
-
391
- .empty-state {
392
- text-align: center;
393
- padding: 3rem 1rem;
394
- color: var(--text-muted);
395
- }
396
-
397
- .empty-state-icon {
398
- font-size: 3rem;
399
- margin-bottom: 1rem;
400
- opacity: 0.5;
401
- }
402
-
403
- /* ── Tabs ── */
404
- .tabs {
405
- display: flex;
406
- gap: 0;
407
- margin-bottom: 2rem;
408
- border-bottom: 1px solid var(--border);
409
- }
410
-
411
- .tab {
412
- padding: 0.75rem 1.5rem;
413
- font-family: 'Outfit', sans-serif;
414
- font-size: 0.9rem;
415
- font-weight: 500;
416
- color: var(--text-muted);
417
- background: none;
418
- border: none;
419
- border-bottom: 2px solid transparent;
420
- cursor: pointer;
421
- transition: all 0.2s ease;
422
- }
423
-
424
- .tab:hover {
425
- color: var(--text-secondary);
426
- }
427
-
428
- .tab.active {
429
- color: var(--accent);
430
- border-bottom-color: var(--accent);
431
- }
432
-
433
- .tab-content {
434
- display: none;
435
- }
436
-
437
- .tab-content.active {
438
- display: block;
439
- animation: fadeIn 0.3s ease;
440
- }
441
-
442
- /* ── API Docs ── */
443
- .docs-section {
444
- margin-bottom: 2rem;
445
- }
446
-
447
- .docs-section h2 {
448
- font-size: 1.25rem;
449
- font-weight: 600;
450
- margin-bottom: 0.75rem;
451
- color: var(--text-primary);
452
- }
453
-
454
- .docs-section h3 {
455
- font-size: 1rem;
456
- font-weight: 500;
457
- margin-top: 1.25rem;
458
- margin-bottom: 0.5rem;
459
- color: var(--text-secondary);
460
- }
461
-
462
- .docs-section p {
463
- color: var(--text-secondary);
464
- font-size: 0.9rem;
465
- margin-bottom: 0.75rem;
466
- line-height: 1.7;
467
- }
468
-
469
- .docs-endpoint {
470
- display: inline-flex;
471
- align-items: center;
472
- gap: 0.5rem;
473
- background: var(--bg-tertiary);
474
- border: 1px solid var(--border);
475
- border-radius: 6px;
476
- padding: 0.5rem 1rem;
477
- margin-bottom: 1rem;
478
- font-family: 'JetBrains Mono', monospace;
479
- font-size: 0.85rem;
480
- }
481
-
482
- .docs-method {
483
- color: var(--success);
484
- font-weight: 600;
485
- }
486
-
487
- .docs-path {
488
- color: var(--text-primary);
489
- }
490
-
491
- .docs-badge {
492
- display: inline-block;
493
- font-size: 0.7rem;
494
- font-weight: 600;
495
- text-transform: uppercase;
496
- letter-spacing: 0.05em;
497
- padding: 0.2rem 0.6rem;
498
- border-radius: 4px;
499
- margin-left: 0.5rem;
500
- }
501
-
502
- .docs-badge.free {
503
- background: var(--success-muted);
504
- color: var(--success);
505
- }
506
-
507
- .docs-badge.limit {
508
- background: var(--accent-muted);
509
- color: var(--accent-hover);
510
- }
511
-
512
- .docs-code-block {
513
- position: relative;
514
- background: var(--bg-tertiary);
515
- border: 1px solid var(--border);
516
- border-radius: 8px;
517
- margin-bottom: 1rem;
518
- overflow: hidden;
519
- }
520
-
521
- .docs-code-header {
522
- display: flex;
523
- align-items: center;
524
- justify-content: space-between;
525
- padding: 0.5rem 1rem;
526
- background: var(--bg-elevated);
527
- border-bottom: 1px solid var(--border);
528
- font-size: 0.75rem;
529
- color: var(--text-muted);
530
- text-transform: uppercase;
531
- letter-spacing: 0.05em;
532
- }
533
-
534
- .docs-copy-btn {
535
- background: none;
536
- border: 1px solid var(--border);
537
- border-radius: 4px;
538
- color: var(--text-muted);
539
- font-size: 0.7rem;
540
- padding: 0.2rem 0.5rem;
541
- cursor: pointer;
542
- font-family: 'Outfit', sans-serif;
543
- transition: all 0.2s ease;
544
- }
545
-
546
- .docs-copy-btn:hover {
547
- color: var(--text-primary);
548
- border-color: var(--border-light);
549
- }
550
-
551
- .docs-code-block pre {
552
- padding: 1rem;
553
- overflow-x: auto;
554
- font-family: 'JetBrains Mono', monospace;
555
- font-size: 0.8rem;
556
- line-height: 1.6;
557
- color: var(--text-primary);
558
- margin: 0;
559
- }
560
-
561
- .docs-table {
562
- width: 100%;
563
- border-collapse: collapse;
564
- font-size: 0.85rem;
565
- margin-bottom: 1rem;
566
- }
567
-
568
- .docs-table th {
569
- text-align: left;
570
- padding: 0.6rem 0.75rem;
571
- background: var(--bg-elevated);
572
- color: var(--text-secondary);
573
- font-weight: 500;
574
- border-bottom: 1px solid var(--border);
575
- font-size: 0.75rem;
576
- text-transform: uppercase;
577
- letter-spacing: 0.05em;
578
- }
579
-
580
- .docs-table td {
581
- padding: 0.6rem 0.75rem;
582
- border-bottom: 1px solid var(--border);
583
- color: var(--text-secondary);
584
- }
585
-
586
- .docs-table tr:last-child td {
587
- border-bottom: none;
588
- }
589
-
590
- .docs-table code {
591
- font-family: 'JetBrains Mono', monospace;
592
- font-size: 0.8rem;
593
- background: var(--bg-tertiary);
594
- padding: 0.15rem 0.4rem;
595
- border-radius: 3px;
596
- color: var(--accent-hover);
597
- }
598
-
599
- .docs-warning {
600
- background: rgba(232, 93, 4, 0.08);
601
- border: 1px solid var(--accent-muted);
602
- border-radius: 8px;
603
- padding: 1rem 1.25rem;
604
- margin-bottom: 1rem;
605
- font-size: 0.85rem;
606
- color: var(--text-secondary);
607
- line-height: 1.7;
608
- }
609
-
610
- .docs-warning strong {
611
- color: var(--accent-hover);
612
- }
613
-
614
- .docs-inline-code {
615
- font-family: 'JetBrains Mono', monospace;
616
- font-size: 0.8rem;
617
- background: var(--bg-tertiary);
618
- padding: 0.15rem 0.4rem;
619
- border-radius: 3px;
620
- color: var(--accent-hover);
621
- }
622
-
623
- .docs-try-it {
624
- background: var(--bg-tertiary);
625
- border: 1px solid var(--border);
626
- border-radius: 8px;
627
- padding: 1.25rem;
628
- margin-top: 1rem;
629
- }
630
-
631
- .docs-try-it textarea {
632
- min-height: 100px;
633
- margin-bottom: 0.75rem;
634
- }
635
-
636
- .docs-try-output {
637
- background: var(--bg-primary);
638
- border: 1px solid var(--border);
639
- border-radius: 6px;
640
- padding: 1rem;
641
- font-family: 'JetBrains Mono', monospace;
642
- font-size: 0.8rem;
643
- color: var(--text-secondary);
644
- white-space: pre-wrap;
645
- word-break: break-word;
646
- max-height: 300px;
647
- overflow-y: auto;
648
- display: none;
649
- }
650
-
651
- .docs-try-output.visible {
652
- display: block;
653
- animation: fadeIn 0.3s ease;
654
- }
655
-
656
- @media (max-width: 600px) {
657
- .container {
658
- padding: 1rem;
659
- }
660
-
661
- .logo {
662
- font-size: 2rem;
663
- }
664
-
665
- .btn-group {
666
- flex-direction: column;
667
- }
668
-
669
- .btn {
670
- width: 100%;
671
- }
672
-
673
- .result-main {
674
- flex-direction: column;
675
- gap: 0.5rem;
676
- text-align: center;
677
- }
678
- }
679
- </style>
680
- </head>
681
- <body>
682
- <div class="container">
683
- <header>
684
- <div class="logo">AI<span>Finder</span></div>
685
- <p class="tagline">Identify which AI provider generated a response</p>
686
- </header>
687
-
688
- <div class="tabs">
689
- <button class="tab active" data-tab="classify">Classify</button>
690
- <button class="tab" data-tab="docs">API Docs</button>
691
- </div>
692
-
693
- <!-- ═══ Classify Tab ═══ -->
694
- <div class="tab-content active" id="tab-classify">
695
- <div class="status-indicator">
696
- <span class="status-dot" id="statusDot"></span>
697
- <span id="statusText">Connecting to API...</span>
698
- </div>
699
-
700
- <div class="card">
701
- <div class="card-label">Paste AI Response</div>
702
- <textarea id="inputText" placeholder="Paste an AI response here to identify which provider generated it..."></textarea>
703
- </div>
704
-
705
- <div class="btn-group">
706
- <button class="btn btn-primary" id="classifyBtn" disabled>
707
- <span id="classifyBtnText">Classify</span>
708
- </button>
709
- <button class="btn btn-secondary" id="clearBtn">Clear</button>
710
- </div>
711
-
712
- <div class="results" id="results">
713
- <div class="card">
714
- <div class="card-label">Result</div>
715
- <div class="result-main">
716
- <span class="result-provider" id="resultProvider">-</span>
717
- <span class="result-confidence" id="resultConfidence">-</span>
718
- </div>
719
- <div class="result-bar">
720
- <div class="result-bar-fill" id="resultBar" style="width: 0%"></div>
721
- </div>
722
- <ul class="result-list" id="resultList"></ul>
723
- </div>
724
-
725
- <div class="correction" id="correction">
726
- <div class="correction-title">Wrong? Correct the provider to train the model:</div>
727
- <select id="providerSelect"></select>
728
- <button class="btn btn-primary" id="trainBtn">Train & Save</button>
729
- </div>
730
- </div>
731
-
732
- <div class="stats" id="stats" style="display: none;">
733
- <div class="stat">
734
- <div class="stat-value" id="correctionsCount">0</div>
735
- <div class="stat-label">Corrections</div>
736
- </div>
737
- <div class="stat">
738
- <div class="stat-value" id="sessionCount">0</div>
739
- <div class="stat-label">Session</div>
740
- </div>
741
- </div>
742
-
743
- <div class="actions" id="actions" style="display: none;">
744
- <button class="btn btn-secondary" id="exportBtn">Export Trained Model</button>
745
- <button class="btn btn-secondary" id="resetBtn">Reset Training</button>
746
- </div>
747
- </div>
748
-
749
- <!-- ═══ API Docs Tab ═══ -->
750
- <div class="tab-content" id="tab-docs">
751
-
752
- <div class="docs-section">
753
- <h2>Public Classification API</h2>
754
- <p>
755
- AIFinder exposes a free, public endpoint for programmatic classification.
756
- No API key required.
757
- </p>
758
- <div>
759
- <div class="docs-endpoint">
760
- <span class="docs-method">POST</span>
761
- <span class="docs-path">/v1/classify</span>
762
- </div>
763
- <span class="docs-badge free">No API Key</span>
764
- <span class="docs-badge limit">60 req/min</span>
765
- </div>
766
- </div>
767
-
768
- <!-- ── Request ── -->
769
- <div class="docs-section">
770
- <h2>Request</h2>
771
- <p>Send a JSON body with <span class="docs-inline-code">Content-Type: application/json</span>.</p>
772
-
773
- <table class="docs-table">
774
- <thead>
775
- <tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr>
776
- </thead>
777
- <tbody>
778
- <tr>
779
- <td><code>text</code></td>
780
- <td>string</td>
781
- <td>Yes</td>
782
- <td>The AI-generated text to classify (min 20 chars)</td>
783
- </tr>
784
- <tr>
785
- <td><code>top_n</code></td>
786
- <td>integer</td>
787
- <td>No</td>
788
- <td>Number of results to return (default: <strong>5</strong>)</td>
789
- </tr>
790
- </tbody>
791
- </table>
792
-
793
- <div class="docs-warning">
794
- <strong>⚠️ Strip thought tags!</strong><br>
795
- Many reasoning models wrap chain-of-thought in
796
- <span class="docs-inline-code">&lt;think&gt;…&lt;/think&gt;</span> or
797
- <span class="docs-inline-code">&lt;thinking&gt;…&lt;/thinking&gt;</span> blocks.
798
- These confuse the classifier. The API strips them automatically, but you should
799
- remove them on your side too to save bandwidth.
800
- </div>
801
- </div>
802
-
803
- <!-- ── Response ── -->
804
- <div class="docs-section">
805
- <h2>Response</h2>
806
- <div class="docs-code-block">
807
- <div class="docs-code-header">
808
- <span>JSON</span>
809
- <button class="docs-copy-btn" onclick="copyCode(this)">Copy</button>
810
- </div>
811
- <pre>{
812
- "provider": "Anthropic",
813
- "confidence": 87.42,
814
- "top_providers": [
815
- { "name": "Anthropic", "confidence": 87.42 },
816
- { "name": "OpenAI", "confidence": 6.15 },
817
- { "name": "Google", "confidence": 3.28 },
818
- { "name": "xAI", "confidence": 1.74 },
819
- { "name": "DeepSeek", "confidence": 0.89 }
820
- ]
821
- }</pre>
822
- </div>
823
-
824
- <table class="docs-table">
825
- <thead>
826
- <tr><th>Field</th><th>Type</th><th>Description</th></tr>
827
- </thead>
828
- <tbody>
829
- <tr>
830
- <td><code>provider</code></td>
831
- <td>string</td>
832
- <td>Best-matching provider name</td>
833
- </tr>
834
- <tr>
835
- <td><code>confidence</code></td>
836
- <td>float</td>
837
- <td>Confidence % for the top provider</td>
838
- </tr>
839
- <tr>
840
- <td><code>top_providers</code></td>
841
- <td>array</td>
842
- <td>Ranked list of <code>{ name, confidence }</code> objects</td>
843
- </tr>
844
- </tbody>
845
- </table>
846
- </div>
847
-
848
- <!-- ── Errors ── -->
849
- <div class="docs-section">
850
- <h2>Errors</h2>
851
- <table class="docs-table">
852
- <thead>
853
- <tr><th>Status</th><th>Meaning</th></tr>
854
- </thead>
855
- <tbody>
856
- <tr><td><code>400</code></td><td>Missing <code>text</code> field or text shorter than 20 characters</td></tr>
857
- <tr><td><code>429</code></td><td>Rate limit exceeded (60 requests/minute per IP)</td></tr>
858
- </tbody>
859
- </table>
860
- </div>
861
-
862
- <!-- ── Code Examples ── -->
863
- <div class="docs-section">
864
- <h2>Code Examples</h2>
865
-
866
- <h3>cURL</h3>
867
- <div class="docs-code-block">
868
- <div class="docs-code-header">
869
- <span>Bash</span>
870
- <button class="docs-copy-btn" onclick="copyCode(this)">Copy</button>
871
- </div>
872
- <pre>curl -X POST https://huggingface.co/spaces/CompactAI/AIFinder/v1/classify \
873
- -H "Content-Type: application/json" \
874
- -d '{
875
- "text": "I would be happy to help you with that! Here is a detailed explanation of how neural networks work...",
876
- "top_n": 5
877
- }'</pre>
878
- </div>
879
-
880
- <h3>Python</h3>
881
- <div class="docs-code-block">
882
- <div class="docs-code-header">
883
- <span>Python</span>
884
- <button class="docs-copy-btn" onclick="copyCode(this)">Copy</button>
885
- </div>
886
- <pre>import re
887
- import requests
888
-
889
- API_URL = "https://huggingface.co/spaces/CompactAI/AIFinder/v1/classify"
890
-
891
- def strip_think_tags(text):
892
- """Remove &lt;think&gt;/&lt;thinking&gt; blocks before classifying."""
893
- return re.sub(r"&lt;think(?:ing)?&gt;.*?&lt;/think(?:ing)?&gt;",
894
- "", text, flags=re.DOTALL).strip()
895
-
896
- text = """I'd be happy to help! Neural networks are
897
- computational models inspired by the human brain..."""
898
-
899
- # Strip thought tags first (the API does this too,
900
- # but saves bandwidth to do it client-side)
901
- cleaned = strip_think_tags(text)
902
-
903
- response = requests.post(API_URL, json={
904
- "text": cleaned,
905
- "top_n": 5
906
- })
907
-
908
- data = response.json()
909
- print(f"Provider: {data['provider']} ({data['confidence']:.1f}%)")
910
- for p in data["top_providers"]:
911
- print(f" {p['name']:&lt;20s} {p['confidence']:5.1f}%")</pre>
912
- </div>
913
-
914
- <h3>JavaScript (fetch)</h3>
915
- <div class="docs-code-block">
916
- <div class="docs-code-header">
917
- <span>JavaScript</span>
918
- <button class="docs-copy-btn" onclick="copyCode(this)">Copy</button>
919
- </div>
920
- <pre>const API_URL = "https://huggingface.co/spaces/CompactAI/AIFinder/v1/classify";
921
-
922
- function stripThinkTags(text) {
923
- return text.replace(/&lt;think(?:ing)?&gt;[\s\S]*?&lt;\/think(?:ing)?&gt;/g, "").trim();
924
- }
925
-
926
- async function classify(text, topN = 5) {
927
- const cleaned = stripThinkTags(text);
928
- const res = await fetch(API_URL, {
929
- method: "POST",
930
- headers: { "Content-Type": "application/json" },
931
- body: JSON.stringify({ text: cleaned, top_n: topN })
932
- });
933
- return res.json();
934
- }
935
-
936
- // Usage
937
- classify("I'd be happy to help you understand...")
938
- .then(data =&gt; {
939
- console.log(`Provider: ${data.provider} (${data.confidence}%)`);
940
- data.top_providers.forEach(p =&gt;
941
- console.log(` ${p.name}: ${p.confidence}%`)
942
- );
943
- });</pre>
944
- </div>
945
-
946
- <h3>Node.js</h3>
947
- <div class="docs-code-block">
948
- <div class="docs-code-header">
949
- <span>JavaScript (Node)</span>
950
- <button class="docs-copy-btn" onclick="copyCode(this)">Copy</button>
951
- </div>
952
- <pre>const API_URL = "https://huggingface.co/spaces/CompactAI/AIFinder/v1/classify";
953
-
954
- async function classify(text, topN = 5) {
955
- const cleaned = text
956
- .replace(/&lt;think(?:ing)?&gt;[\s\S]*?&lt;\/think(?:ing)?&gt;/g, "")
957
- .trim();
958
-
959
- const res = await fetch(API_URL, {
960
- method: "POST",
961
- headers: { "Content-Type": "application/json" },
962
- body: JSON.stringify({ text: cleaned, top_n: topN })
963
- });
964
-
965
- if (!res.ok) {
966
- const err = await res.json();
967
- throw new Error(err.error || `HTTP ${res.status}`);
968
- }
969
- return res.json();
970
- }
971
-
972
- // Example
973
- (async () =&gt; {
974
- const result = await classify(
975
- "Let me think about this step by step...",
976
- 3
977
- );
978
- console.log(result);
979
- })();</pre>
980
- </div>
981
- </div>
982
-
983
- <!-- ── Try It ── -->
984
- <div class="docs-section">
985
- <h2>Try It</h2>
986
- <p>Test the API right here — paste any AI-generated text and hit Send.</p>
987
- <div class="docs-try-it">
988
- <textarea id="docsTestInput" placeholder="Paste AI-generated text here..."></textarea>
989
- <div class="btn-group">
990
- <button class="btn btn-primary" id="docsTestBtn">Send Request</button>
991
- </div>
992
- <div class="docs-try-output" id="docsTestOutput"></div>
993
- </div>
994
- </div>
995
-
996
- <!-- ── Providers ── -->
997
- <div class="docs-section">
998
- <h2>Supported Providers</h2>
999
- <p>The classifier currently supports these providers:</p>
1000
- <div id="docsProviderList" style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem;"></div>
1001
- </div>
1002
- </div>
1003
-
1004
- <div class="footer">
1005
- <p>AIFinder &mdash; Train on corrections to improve accuracy</p>
1006
- <p style="margin-top: 0.5rem;">
1007
- Want to contribute? Test this and post to the
1008
- <a href="https://huggingface.co/spaces" target="_blank">HuggingFace Spaces Community</a>
1009
- if you want it merged!
1010
- </p>
1011
- </div>
1012
- </div>
1013
-
1014
- <div class="toast" id="toast"></div>
1015
-
1016
- <script>
1017
- const API_BASE = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
1018
- ? 'http://localhost:7860'
1019
- : '';
1020
-
1021
- let providers = [];
1022
- let correctionsCount = 0;
1023
- let sessionCorrections = 0;
1024
-
1025
- const inputText = document.getElementById('inputText');
1026
- const classifyBtn = document.getElementById('classifyBtn');
1027
- const classifyBtnText = document.getElementById('classifyBtnText');
1028
- const clearBtn = document.getElementById('clearBtn');
1029
- const results = document.getElementById('results');
1030
- const resultProvider = document.getElementById('resultProvider');
1031
- const resultConfidence = document.getElementById('resultConfidence');
1032
- const resultBar = document.getElementById('resultBar');
1033
- const resultList = document.getElementById('resultList');
1034
- const correction = document.getElementById('correction');
1035
- const providerSelect = document.getElementById('providerSelect');
1036
- const trainBtn = document.getElementById('trainBtn');
1037
- const stats = document.getElementById('stats');
1038
- const correctionsCountEl = document.getElementById('correctionsCount');
1039
- const sessionCountEl = document.getElementById('sessionCount');
1040
- const actions = document.getElementById('actions');
1041
- const exportBtn = document.getElementById('exportBtn');
1042
- const resetBtn = document.getElementById('resetBtn');
1043
- const toast = document.getElementById('toast');
1044
- const statusDot = document.getElementById('statusDot');
1045
- const statusText = document.getElementById('statusText');
1046
-
1047
- function showToast(message, type = 'info') {
1048
- toast.textContent = message;
1049
- toast.className = 'toast visible' + (type === 'success' ? ' success' : '');
1050
- setTimeout(() => {
1051
- toast.classList.remove('visible');
1052
- }, 3000);
1053
- }
1054
-
1055
- async function checkStatus() {
1056
- try {
1057
- const res = await fetch(`${API_BASE}/api/status`);
1058
- const data = await res.json();
1059
- if (data.loaded) {
1060
- statusDot.classList.remove('loading');
1061
- statusText.textContent = `Ready (${data.device})`;
1062
- classifyBtn.disabled = false;
1063
- loadProviders();
1064
- loadStats();
1065
- } else {
1066
- setTimeout(checkStatus, 1000);
1067
- }
1068
- } catch (e) {
1069
- statusDot.classList.add('loading');
1070
- statusText.textContent = 'Connecting to API...';
1071
- setTimeout(checkStatus, 2000);
1072
- }
1073
- }
1074
-
1075
- async function loadProviders() {
1076
- const res = await fetch(`${API_BASE}/api/providers`);
1077
- const data = await res.json();
1078
- providers = data.providers;
1079
-
1080
- providerSelect.innerHTML = providers.map(p =>
1081
- `<option value="${p}">${p}</option>`
1082
- ).join('');
1083
- }
1084
-
1085
- function loadStats() {
1086
- const saved = localStorage.getItem('aifinder_corrections');
1087
- if (saved) {
1088
- correctionsCount = parseInt(saved, 10);
1089
- correctionsCountEl.textContent = correctionsCount;
1090
- stats.style.display = 'flex';
1091
- actions.style.display = 'flex';
1092
- }
1093
- sessionCountEl.textContent = sessionCorrections;
1094
- }
1095
-
1096
- function saveStats() {
1097
- localStorage.setItem('aifinder_corrections', correctionsCount.toString());
1098
- }
1099
-
1100
- async function classify() {
1101
- const text = inputText.value.trim();
1102
- if (text.length < 20) {
1103
- showToast('Text must be at least 20 characters');
1104
- return;
1105
- }
1106
-
1107
- classifyBtn.disabled = true;
1108
- classifyBtnText.innerHTML = '<span class="loading"></span>';
1109
-
1110
- try {
1111
- const res = await fetch(`${API_BASE}/api/classify`, {
1112
- method: 'POST',
1113
- headers: { 'Content-Type': 'application/json' },
1114
- body: JSON.stringify({ text })
1115
- });
1116
-
1117
- if (!res.ok) {
1118
- throw new Error('Classification failed');
1119
- }
1120
-
1121
- const data = await res.json();
1122
- showResults(data);
1123
- } catch (e) {
1124
- showToast('Error: ' + e.message);
1125
- } finally {
1126
- classifyBtn.disabled = false;
1127
- classifyBtnText.textContent = 'Classify';
1128
- }
1129
- }
1130
-
1131
- function showResults(data) {
1132
- resultProvider.textContent = data.provider;
1133
- resultConfidence.textContent = data.confidence.toFixed(1) + '%';
1134
- resultBar.style.width = data.confidence + '%';
1135
-
1136
- resultList.innerHTML = data.top_providers.map(p => `
1137
- <li class="result-item">
1138
- <span class="result-name">${p.name}</span>
1139
- <span class="result-percent">${p.confidence.toFixed(1)}%</span>
1140
- </li>
1141
- `).join('');
1142
-
1143
- providerSelect.value = data.provider;
1144
-
1145
- results.classList.add('visible');
1146
- correction.classList.add('visible');
1147
-
1148
- if (correctionsCount > 0 || sessionCorrections > 0) {
1149
- stats.style.display = 'flex';
1150
- actions.style.display = 'flex';
1151
- }
1152
- }
1153
-
1154
- async function train() {
1155
- const text = inputText.value.trim();
1156
- const correctProvider = providerSelect.value;
1157
-
1158
- trainBtn.disabled = true;
1159
- trainBtn.innerHTML = '<span class="loading"></span>';
1160
-
1161
- try {
1162
- const res = await fetch(`${API_BASE}/api/correct`, {
1163
- method: 'POST',
1164
- headers: { 'Content-Type': 'application/json' },
1165
- body: JSON.stringify({ text, correct_provider: correctProvider })
1166
- });
1167
-
1168
- if (!res.ok) {
1169
- throw new Error('Training failed');
1170
- }
1171
-
1172
- const data = await res.json();
1173
- correctionsCount++;
1174
- sessionCorrections++;
1175
- saveStats();
1176
- correctionsCountEl.textContent = correctionsCount;
1177
- sessionCountEl.textContent = sessionCorrections;
1178
-
1179
- showToast(`Trained! Loss: ${data.loss.toFixed(4)}`, 'success');
1180
-
1181
- stats.style.display = 'flex';
1182
- actions.style.display = 'flex';
1183
-
1184
- classify();
1185
- } catch (e) {
1186
- showToast('Error: ' + e.message);
1187
- } finally {
1188
- trainBtn.disabled = false;
1189
- trainBtn.textContent = 'Train & Save';
1190
- }
1191
- }
1192
-
1193
- async function exportModel() {
1194
- exportBtn.disabled = true;
1195
- exportBtn.innerHTML = '<span class="loading"></span>';
1196
-
1197
- try {
1198
- const res = await fetch(`${API_BASE}/api/save`, {
1199
- method: 'POST',
1200
- headers: { 'Content-Type': 'application/json' },
1201
- body: JSON.stringify({ filename: 'aifinder_trained.pt' })
1202
- });
1203
-
1204
- if (!res.ok) {
1205
- throw new Error('Save failed');
1206
- }
1207
-
1208
- const data = await res.json();
1209
-
1210
- const link = document.createElement('a');
1211
- link.href = `${API_BASE}/models/${data.filename}`;
1212
- link.download = data.filename;
1213
- link.click();
1214
-
1215
- showToast('Model exported!', 'success');
1216
- } catch (e) {
1217
- showToast('Error: ' + e.message);
1218
- } finally {
1219
- exportBtn.disabled = false;
1220
- exportBtn.textContent = 'Export Trained Model';
1221
- }
1222
- }
1223
-
1224
- function resetTraining() {
1225
- if (!confirm('Reset all training data? This cannot be undone.')) {
1226
- return;
1227
- }
1228
-
1229
- correctionsCount = 0;
1230
- sessionCorrections = 0;
1231
- localStorage.removeItem('aifinder_corrections');
1232
- correctionsCountEl.textContent = '0';
1233
- sessionCountEl.textContent = '0';
1234
- stats.style.display = 'none';
1235
- actions.style.display = 'none';
1236
- showToast('Training data reset');
1237
- }
1238
-
1239
- classifyBtn.addEventListener('click', classify);
1240
- clearBtn.addEventListener('click', () => {
1241
- inputText.value = '';
1242
- results.classList.remove('visible');
1243
- correction.classList.remove('visible');
1244
- });
1245
- trainBtn.addEventListener('click', train);
1246
- exportBtn.addEventListener('click', exportModel);
1247
- resetBtn.addEventListener('click', resetTraining);
1248
-
1249
- inputText.addEventListener('keydown', (e) => {
1250
- if (e.key === 'Enter' && e.ctrlKey) {
1251
- classify();
1252
- }
1253
- });
1254
-
1255
- // ── Tab switching ──
1256
- document.querySelectorAll('.tab').forEach(tab => {
1257
- tab.addEventListener('click', () => {
1258
- document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
1259
- document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
1260
- tab.classList.add('active');
1261
- document.getElementById('tab-' + tab.dataset.tab).classList.add('active');
1262
- });
1263
- });
1264
-
1265
- // ── Copy button for code blocks ──
1266
- function copyCode(btn) {
1267
- const pre = btn.closest('.docs-code-block').querySelector('pre');
1268
- navigator.clipboard.writeText(pre.textContent).then(() => {
1269
- btn.textContent = 'Copied!';
1270
- setTimeout(() => { btn.textContent = 'Copy'; }, 1500);
1271
- });
1272
- }
1273
-
1274
- // ── Docs: populate provider badges ──
1275
- function populateDocsProviders() {
1276
- const list = document.getElementById('docsProviderList');
1277
- if (!list || !providers.length) return;
1278
- list.innerHTML = providers.map(p =>
1279
- `<span class="docs-inline-code" style="padding:0.3rem 0.75rem;">${p}</span>`
1280
- ).join('');
1281
- }
1282
-
1283
- // ── Docs: "Try It" live tester ──
1284
- const docsTestBtn = document.getElementById('docsTestBtn');
1285
- const docsTestInput = document.getElementById('docsTestInput');
1286
- const docsTestOutput = document.getElementById('docsTestOutput');
1287
-
1288
- if (docsTestBtn) {
1289
- docsTestBtn.addEventListener('click', async () => {
1290
- const text = docsTestInput.value.trim();
1291
- if (text.length < 20) {
1292
- docsTestOutput.textContent = '{"error": "Text too short (minimum 20 characters)"}';
1293
- docsTestOutput.classList.add('visible');
1294
- return;
1295
- }
1296
- docsTestBtn.disabled = true;
1297
- docsTestBtn.innerHTML = '<span class="loading"></span>';
1298
- try {
1299
- const res = await fetch(`${API_BASE}/v1/classify`, {
1300
- method: 'POST',
1301
- headers: { 'Content-Type': 'application/json' },
1302
- body: JSON.stringify({ text, top_n: 5 })
1303
- });
1304
- const data = await res.json();
1305
- docsTestOutput.textContent = JSON.stringify(data, null, 2);
1306
- } catch (e) {
1307
- docsTestOutput.textContent = `{"error": "${e.message}"}`;
1308
- }
1309
- docsTestOutput.classList.add('visible');
1310
- docsTestBtn.disabled = false;
1311
- docsTestBtn.textContent = 'Send Request';
1312
- });
1313
- }
1314
-
1315
- // Hook provider list population into the existing load flow
1316
- const _origLoadProviders = loadProviders;
1317
- loadProviders = async function() {
1318
- await _origLoadProviders();
1319
- populateDocsProviders();
1320
- };
1321
-
1322
- checkStatus();
1323
- </script>
1324
- </body>
1325
- </html>