armand0e commited on
Commit
49687c1
·
1 Parent(s): 710ace6

Reimplement Openrouter filter

Browse files
src/app/api/openrouter-models/route.ts CHANGED
@@ -3,6 +3,12 @@ import { NextResponse } from "next/server";
3
  interface OpenRouterModel {
4
  id: string;
5
  name: string;
 
 
 
 
 
 
6
  pricing: {
7
  prompt: string;
8
  completion: string;
@@ -10,6 +16,29 @@ interface OpenRouterModel {
10
  context_length: number;
11
  }
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  let cachedModels: { id: string; name: string }[] | null = null;
14
  let cacheTimestamp: number = 0;
15
  const CACHE_DURATION = 1000 * 60 * 60; // 1 hour
@@ -34,10 +63,20 @@ export async function GET() {
34
 
35
  const data = await response.json();
36
 
37
- cachedModels = data.data.map((model: OpenRouterModel) => ({
38
- id: model.id,
39
- name: model.name || model.id,
40
- })).sort((a: { name: string }, b: { name: string }) => a.name.localeCompare(b.name));
 
 
 
 
 
 
 
 
 
 
41
 
42
  cacheTimestamp = now;
43
 
 
3
  interface OpenRouterModel {
4
  id: string;
5
  name: string;
6
+ created_at?: string | number;
7
+ createdAt?: string | number;
8
+ created?: string | number;
9
+ released_at?: string | number;
10
+ releasedAt?: string | number;
11
+ release_date?: string | number;
12
  pricing: {
13
  prompt: string;
14
  completion: string;
 
16
  context_length: number;
17
  }
18
 
19
+ function toTimestampMs(value: unknown): number {
20
+ if (typeof value === "number") {
21
+ // Heuristic: treat small values as seconds
22
+ return value > 1_000_000_000_000 ? value : value * 1000;
23
+ }
24
+ if (typeof value === "string") {
25
+ const parsed = Date.parse(value);
26
+ return Number.isFinite(parsed) ? parsed : 0;
27
+ }
28
+ return 0;
29
+ }
30
+
31
+ function getModelTimestampMs(model: OpenRouterModel): number {
32
+ return Math.max(
33
+ toTimestampMs(model.released_at),
34
+ toTimestampMs(model.releasedAt),
35
+ toTimestampMs(model.release_date),
36
+ toTimestampMs(model.created_at),
37
+ toTimestampMs(model.createdAt),
38
+ toTimestampMs(model.created)
39
+ );
40
+ }
41
+
42
  let cachedModels: { id: string; name: string }[] | null = null;
43
  let cacheTimestamp: number = 0;
44
  const CACHE_DURATION = 1000 * 60 * 60; // 1 hour
 
63
 
64
  const data = await response.json();
65
 
66
+ cachedModels = data.data
67
+ .slice()
68
+ .sort((a: OpenRouterModel, b: OpenRouterModel) => {
69
+ const bt = getModelTimestampMs(b);
70
+ const at = getModelTimestampMs(a);
71
+ if (bt !== at) return bt - at;
72
+ const an = (a.name || a.id).toLowerCase();
73
+ const bn = (b.name || b.id).toLowerCase();
74
+ return an.localeCompare(bn);
75
+ })
76
+ .map((model: OpenRouterModel) => ({
77
+ id: model.id,
78
+ name: model.name || model.id,
79
+ }));
80
 
81
  cacheTimestamp = now;
82
 
src/app/page.tsx CHANGED
@@ -69,16 +69,7 @@ const STUDENT_MODELS = [
69
  "Other",
70
  ];
71
 
72
- const SOURCE_MODELS = [
73
- "google/gemini-3-flash-preview",
74
- "openai/gpt-4o-mini",
75
- "openai/gpt-4o",
76
- "anthropic/claude-3-5-sonnet",
77
- "deepseek/deepseek-r1",
78
- "Qwen/Qwen2.5-72B-Instruct",
79
- "meta-llama/Llama-3.1-8B-Instruct",
80
- "Other (Huggingface Models)",
81
- ];
82
 
83
  const REASONING_DEPTHS = ["low", "medium", "high"];
84
  const DATASET_SIZES = ["100x", "250x", "500x", "1000x", "3000x", "11000x"];
@@ -114,6 +105,10 @@ export default function Home() {
114
  const [hfModelQuery, setHfModelQuery] = useState("");
115
  const [loadingHfModels, setLoadingHfModels] = useState(false);
116
 
 
 
 
 
117
  // Distillation form state
118
  const [sourceDataset, setSourceDataset] = useState("");
119
  const [sourceDatasetOther, setSourceDatasetOther] = useState("");
@@ -165,6 +160,36 @@ export default function Home() {
165
  };
166
  }, [hfModelQuery]);
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  async function fetchRequests() {
169
  try {
170
  const [distillRes, datasetRes] = await Promise.all([
@@ -233,6 +258,8 @@ export default function Home() {
233
  setSourceDatasetOther("");
234
  setStudentModel("");
235
  setStudentModelOther("");
 
 
236
  setDistillNotes("");
237
  setShowDistillForm(false);
238
  fetchRequests();
@@ -249,7 +276,7 @@ export default function Home() {
249
  async function handleDatasetSubmit(e: React.FormEvent) {
250
  e.preventDefault();
251
  const resolvedSourceModel =
252
- sourceModel === "Other (Huggingface Models)" ? sourceModelOther.trim() : sourceModel;
253
  if (!resolvedSourceModel) {
254
  toast({ title: "Error", description: "Please select a source model", variant: "destructive" });
255
  return;
@@ -269,8 +296,10 @@ export default function Home() {
269
  });
270
  if (res.ok) {
271
  toast({ title: "Success", description: "Dataset request submitted!" });
272
- setSourceModel("google/gemini-3-flash-preview");
273
  setSourceModelOther("");
 
 
274
  setDatasetSize("250x");
275
  setReasoningDepth("high");
276
  setSelectedTopics([]);
@@ -433,7 +462,15 @@ export default function Home() {
433
  value={studentModel}
434
  onValueChange={(v) => {
435
  setStudentModel(v);
436
- if (v !== "Other") setStudentModelOther("");
 
 
 
 
 
 
 
 
437
  }}
438
  >
439
  <SelectTrigger>
@@ -463,13 +500,18 @@ export default function Home() {
463
  )}
464
  {studentModel === "Other" && (
465
  <div className="space-y-2">
466
- <Label htmlFor="studentOther">Student Model (Other) *</Label>
467
- <input
468
- id="studentOther"
469
- className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
470
  value={studentModelOther}
471
- onChange={(e) => setStudentModelOther(e.target.value)}
472
- placeholder="Type the student model name"
 
 
 
 
 
 
473
  />
474
  </div>
475
  )}
@@ -581,19 +623,23 @@ export default function Home() {
581
  <div className="space-y-2">
582
  <Label htmlFor="sourceModel">Source Model *</Label>
583
  <Combobox
584
- options={SOURCE_MODELS.map((m) => ({ id: m, name: m }))}
 
 
 
585
  value={sourceModel}
586
  onValueChange={(v) => {
587
  setSourceModel(v);
588
- if (v !== "Other (Huggingface Models)") {
589
  setSourceModelOther("");
590
  setHfModelQuery("");
591
  setHuggingfaceModels([]);
592
  }
593
  }}
594
  placeholder="Select a model"
595
- searchPlaceholder="Search openrouter models..."
596
  emptyMessage="No models found"
 
597
  />
598
  </div>
599
  <div className="space-y-2">
@@ -612,7 +658,7 @@ export default function Home() {
612
  </Select>
613
  </div>
614
  </div>
615
- {sourceModel === "Other (Huggingface Models)" && (
616
  <div className="space-y-2">
617
  <Label htmlFor="sourceModelOther">Hugging Face Model *</Label>
618
  <Combobox
 
69
  "Other",
70
  ];
71
 
72
+ const SOURCE_MODEL_OTHER_HF = "Other (HuggingFace)";
 
 
 
 
 
 
 
 
 
73
 
74
  const REASONING_DEPTHS = ["low", "medium", "high"];
75
  const DATASET_SIZES = ["100x", "250x", "500x", "1000x", "3000x", "11000x"];
 
105
  const [hfModelQuery, setHfModelQuery] = useState("");
106
  const [loadingHfModels, setLoadingHfModels] = useState(false);
107
 
108
+ const [distillHuggingfaceModels, setDistillHuggingfaceModels] = useState<HuggingFaceModel[]>([]);
109
+ const [distillHfModelQuery, setDistillHfModelQuery] = useState("");
110
+ const [loadingDistillHfModels, setLoadingDistillHfModels] = useState(false);
111
+
112
  // Distillation form state
113
  const [sourceDataset, setSourceDataset] = useState("");
114
  const [sourceDatasetOther, setSourceDatasetOther] = useState("");
 
160
  };
161
  }, [hfModelQuery]);
162
 
163
+ useEffect(() => {
164
+ let cancelled = false;
165
+ const q = distillHfModelQuery.trim();
166
+
167
+ const timer = setTimeout(async () => {
168
+ setLoadingDistillHfModels(true);
169
+ try {
170
+ const res = await fetch(`/api/huggingface-models?q=${encodeURIComponent(q)}&limit=20`);
171
+ const data = await res.json();
172
+ if (!cancelled) {
173
+ setDistillHuggingfaceModels(Array.isArray(data) ? data : []);
174
+ }
175
+ } catch (error) {
176
+ console.error("Error fetching Hugging Face models:", error);
177
+ if (!cancelled) {
178
+ setDistillHuggingfaceModels([]);
179
+ }
180
+ } finally {
181
+ if (!cancelled) {
182
+ setLoadingDistillHfModels(false);
183
+ }
184
+ }
185
+ }, 250);
186
+
187
+ return () => {
188
+ cancelled = true;
189
+ clearTimeout(timer);
190
+ };
191
+ }, [distillHfModelQuery]);
192
+
193
  async function fetchRequests() {
194
  try {
195
  const [distillRes, datasetRes] = await Promise.all([
 
258
  setSourceDatasetOther("");
259
  setStudentModel("");
260
  setStudentModelOther("");
261
+ setDistillHfModelQuery("");
262
+ setDistillHuggingfaceModels([]);
263
  setDistillNotes("");
264
  setShowDistillForm(false);
265
  fetchRequests();
 
276
  async function handleDatasetSubmit(e: React.FormEvent) {
277
  e.preventDefault();
278
  const resolvedSourceModel =
279
+ sourceModel === SOURCE_MODEL_OTHER_HF ? sourceModelOther.trim() : sourceModel;
280
  if (!resolvedSourceModel) {
281
  toast({ title: "Error", description: "Please select a source model", variant: "destructive" });
282
  return;
 
296
  });
297
  if (res.ok) {
298
  toast({ title: "Success", description: "Dataset request submitted!" });
299
+ setSourceModel(openrouterModels[0]?.id || "");
300
  setSourceModelOther("");
301
+ setHfModelQuery("");
302
+ setHuggingfaceModels([]);
303
  setDatasetSize("250x");
304
  setReasoningDepth("high");
305
  setSelectedTopics([]);
 
462
  value={studentModel}
463
  onValueChange={(v) => {
464
  setStudentModel(v);
465
+ if (v !== "Other") {
466
+ setStudentModelOther("");
467
+ setDistillHfModelQuery("");
468
+ setDistillHuggingfaceModels([]);
469
+ } else {
470
+ setStudentModelOther("");
471
+ setDistillHfModelQuery("");
472
+ setDistillHuggingfaceModels([]);
473
+ }
474
  }}
475
  >
476
  <SelectTrigger>
 
500
  )}
501
  {studentModel === "Other" && (
502
  <div className="space-y-2">
503
+ <Label htmlFor="studentOther">Hugging Face Student Model *</Label>
504
+ <Combobox
505
+ options={distillHuggingfaceModels}
 
506
  value={studentModelOther}
507
+ onValueChange={setStudentModelOther}
508
+ placeholder="Search safetensors models"
509
+ searchPlaceholder="Type to search models..."
510
+ emptyMessage={distillHfModelQuery.trim() ? "No models found" : "Start typing to search"}
511
+ loading={loadingDistillHfModels}
512
+ searchValue={distillHfModelQuery}
513
+ onSearchValueChange={setDistillHfModelQuery}
514
+ disableLocalFilter
515
  />
516
  </div>
517
  )}
 
623
  <div className="space-y-2">
624
  <Label htmlFor="sourceModel">Source Model *</Label>
625
  <Combobox
626
+ options={[
627
+ ...openrouterModels,
628
+ { id: SOURCE_MODEL_OTHER_HF, name: SOURCE_MODEL_OTHER_HF },
629
+ ]}
630
  value={sourceModel}
631
  onValueChange={(v) => {
632
  setSourceModel(v);
633
+ if (v !== SOURCE_MODEL_OTHER_HF) {
634
  setSourceModelOther("");
635
  setHfModelQuery("");
636
  setHuggingfaceModels([]);
637
  }
638
  }}
639
  placeholder="Select a model"
640
+ searchPlaceholder="Search OpenRouter models..."
641
  emptyMessage="No models found"
642
+ loading={loadingModels}
643
  />
644
  </div>
645
  <div className="space-y-2">
 
658
  </Select>
659
  </div>
660
  </div>
661
+ {sourceModel === SOURCE_MODEL_OTHER_HF && (
662
  <div className="space-y-2">
663
  <Label htmlFor="sourceModelOther">Hugging Face Model *</Label>
664
  <Combobox