react-openrouter-model-picker 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,473 @@
1
+ import { jsxs as a, jsx as o } from "react/jsx-runtime";
2
+ import { useMemo as U, useState as N, useRef as T, useEffect as k, useCallback as ue } from "react";
3
+ function Q(e) {
4
+ return e == null ? "—" : e === 0 ? "$0" : e < 0.01 ? `$${e.toFixed(4)}` : `$${e.toFixed(2)}`;
5
+ }
6
+ function G(e) {
7
+ if (!e) return "—";
8
+ if (e >= 1e6) {
9
+ const r = (e / 1e6).toFixed(1);
10
+ return `${r.endsWith(".0") ? r.slice(0, -2) : r}M`;
11
+ }
12
+ return e >= 1e3 ? `${Math.round(e / 1e3)}k` : String(e);
13
+ }
14
+ const me = {
15
+ openai: "OpenAI",
16
+ anthropic: "Anthropic",
17
+ google: "Google",
18
+ "meta-llama": "Meta",
19
+ mistralai: "Mistral",
20
+ perplexity: "Perplexity",
21
+ "x-ai": "xAI",
22
+ deepseek: "DeepSeek",
23
+ qwen: "Qwen",
24
+ cohere: "Cohere",
25
+ amazon: "Amazon",
26
+ microsoft: "Microsoft",
27
+ nvidia: "NVIDIA",
28
+ moonshotai: "Moonshot",
29
+ "z-ai": "Z.ai",
30
+ openrouter: "OpenRouter"
31
+ };
32
+ function H(e, r) {
33
+ return r?.[e] ?? me[e] ?? e.replace(/-/g, " ").replace(/\b\w/g, (s) => s.toUpperCase());
34
+ }
35
+ function ne(e) {
36
+ return e.pricing.promptPerM * 0.75 + e.pricing.completionPerM * 0.25 + e.pricing.perRequest * 1e3;
37
+ }
38
+ const he = {
39
+ placeholder: "Select a model…",
40
+ searchPlaceholder: "Search: gpt-5, claude, sonar, gemini, llama…",
41
+ allVendors: "All vendors",
42
+ sortName: "Name",
43
+ sortCheapest: "Cheapest",
44
+ sortNewest: "Newest",
45
+ sortContext: "Context",
46
+ refresh: "Refresh prices",
47
+ refreshing: "refreshing…",
48
+ loading: "Loading models from OpenRouter…",
49
+ loadError: "Could not load the model list",
50
+ noMatches: (e) => `No models match “${e}”.`,
51
+ countOf: (e, r) => `${e} of ${r}`,
52
+ modelsCount: (e) => `${e} OpenRouter models`,
53
+ pricesFrom: (e) => `prices as of ${e}`,
54
+ auto: "Automatic (server default)",
55
+ now: "now:",
56
+ isDefault: "default",
57
+ free: "free",
58
+ web: "web",
59
+ json: "json",
60
+ reasoning: "reasoning",
61
+ context: "context",
62
+ maxOutput: "max output",
63
+ perRequest: "per request",
64
+ priceTitle: "USD per 1M tokens: input / output",
65
+ webTitle: "Built-in web search",
66
+ jsonTitle: "Supports response_format JSON"
67
+ }, ke = {
68
+ placeholder: "Wybierz model…",
69
+ searchPlaceholder: "Szukaj: gpt-5, claude, sonar, gemini, llama…",
70
+ allVendors: "Wszyscy producenci",
71
+ sortName: "Nazwa",
72
+ sortCheapest: "Najtańsze",
73
+ sortNewest: "Najnowsze",
74
+ sortContext: "Kontekst",
75
+ refresh: "Odśwież ceny",
76
+ refreshing: "odświeżam…",
77
+ loading: "Pobieram listę modeli z OpenRoutera…",
78
+ loadError: "Nie udało się pobrać listy modeli",
79
+ noMatches: (e) => `Brak modeli pasujących do „${e}”.`,
80
+ countOf: (e, r) => `${e} z ${r}`,
81
+ modelsCount: (e) => `${e} modeli OpenRouter`,
82
+ pricesFrom: (e) => `ceny z ${e}`,
83
+ auto: "Automatycznie (domyślny z serwera)",
84
+ now: "teraz:",
85
+ isDefault: "domyślny",
86
+ free: "darmowy",
87
+ web: "web",
88
+ json: "json",
89
+ reasoning: "reasoning",
90
+ context: "kontekst",
91
+ maxOutput: "max wyjście",
92
+ perRequest: "za żądanie",
93
+ priceTitle: "USD za 1M tokenów: wejście / wyjście",
94
+ webTitle: "Wbudowane wyszukiwanie w sieci",
95
+ jsonTitle: "Obsługuje response_format JSON"
96
+ };
97
+ function _e(e, r, s) {
98
+ if (!r) return !0;
99
+ const l = `${e.id} ${e.name} ${H(e.vendor, s)}`.toLowerCase();
100
+ return r.split(/\s+/).every((c) => l.includes(c));
101
+ }
102
+ function fe(e) {
103
+ if (!e) return null;
104
+ const r = typeof e == "string" ? new Date(e) : e;
105
+ return Number.isNaN(r.getTime()) ? null : r.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
106
+ }
107
+ function Re(e) {
108
+ const {
109
+ models: r,
110
+ value: s,
111
+ onChange: l,
112
+ loading: c,
113
+ refreshing: p,
114
+ error: g,
115
+ onRefresh: u,
116
+ fetchedAt: R,
117
+ defaultModelId: w,
118
+ allowAuto: z,
119
+ label: x,
120
+ compact: D,
121
+ disabled: q,
122
+ vendorLabels: M,
123
+ filter: v,
124
+ initialSort: b = "name",
125
+ theme: $,
126
+ className: A,
127
+ id: L
128
+ } = e, n = U(() => ({ ...he, ...e.labels ?? {} }), [e.labels]), [m, _] = N(!1), [O, Z] = N(""), [S, ae] = N("all"), [j, ie] = N(b), [F, E] = N(0), K = T(null), X = T(null), Y = T(null), y = U(() => v ? r.filter(v) : r, [r, v]), d = s ? y.find((t) => t.id === s) ?? r.find((t) => t.id === s) : void 0, le = U(() => {
129
+ const t = /* @__PURE__ */ new Map();
130
+ for (const i of y) t.set(i.vendor, (t.get(i.vendor) ?? 0) + 1);
131
+ return [...t.entries()].sort((i, W) => W[1] - i[1] || i[0].localeCompare(W[0]));
132
+ }, [y]), C = U(() => {
133
+ const t = O.trim().toLowerCase(), i = y.filter((h) => (S === "all" || h.vendor === S) && _e(h, t, M)), W = {
134
+ name: (h, f) => h.name.localeCompare(f.name),
135
+ cheapest: (h, f) => ne(h) - ne(f) || h.name.localeCompare(f.name),
136
+ newest: (h, f) => f.created - h.created || h.name.localeCompare(f.name),
137
+ context: (h, f) => (f.contextLength ?? 0) - (h.contextLength ?? 0) || h.name.localeCompare(f.name)
138
+ };
139
+ return [...i].sort(W[j]);
140
+ }, [y, O, S, j, M]);
141
+ k(() => {
142
+ if (!m) return;
143
+ const t = (i) => {
144
+ K.current && !K.current.contains(i.target) && _(!1);
145
+ };
146
+ return document.addEventListener("mousedown", t), () => document.removeEventListener("mousedown", t);
147
+ }, [m]), k(() => {
148
+ E(0);
149
+ }, [O, S, j]), k(() => {
150
+ if (!m) return;
151
+ Y.current?.focus();
152
+ const t = C.findIndex((i) => i.id === s);
153
+ t >= 0 && E(t);
154
+ }, [m]), k(() => {
155
+ X.current?.querySelector(`[data-index="${F}"]`)?.scrollIntoView?.({ block: "nearest" });
156
+ }, [F, m]);
157
+ const ee = (t) => {
158
+ l(t), _(!1), Z("");
159
+ }, ce = (t) => {
160
+ if (t.key === "ArrowDown")
161
+ t.preventDefault(), E((i) => Math.min(C.length - 1, i + 1));
162
+ else if (t.key === "ArrowUp")
163
+ t.preventDefault(), E((i) => Math.max(0, i - 1));
164
+ else if (t.key === "Enter") {
165
+ t.preventDefault();
166
+ const i = C[F];
167
+ i && ee(i.id);
168
+ } else t.key === "Escape" && _(!1);
169
+ }, re = !!z && !s, pe = d ? d.name : s || (re ? n.auto : n.placeholder), te = fe(R), I = g ? g instanceof Error ? g.message : String(g) : null, de = ["orp", m && "orp--open", D && "orp--compact", x && "orp--has-label", A].filter(Boolean).join(" ");
170
+ return /* @__PURE__ */ a("div", { ref: K, className: de, "data-theme": $, id: L, children: [
171
+ x && /* @__PURE__ */ a("div", { className: "orp__label", children: [
172
+ /* @__PURE__ */ o("span", { children: x }),
173
+ /* @__PURE__ */ a("span", { className: "orp__label-meta", children: [
174
+ r.length ? n.modelsCount(r.length) : "",
175
+ te && ` · ${n.pricesFrom(te)}`
176
+ ] })
177
+ ] }),
178
+ /* @__PURE__ */ a("div", { className: "orp__anchor", children: [
179
+ /* @__PURE__ */ o(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ className: "orp__summary",
184
+ disabled: q,
185
+ "aria-haspopup": "listbox",
186
+ "aria-expanded": m,
187
+ onClick: () => _((t) => !t),
188
+ children: /* @__PURE__ */ a("div", { className: "orp__summary-inner", children: [
189
+ /* @__PURE__ */ a("div", { className: "orp__summary-text", children: [
190
+ /* @__PURE__ */ o("p", { className: "orp__summary-title", children: pe }),
191
+ d ? /* @__PURE__ */ o("p", { className: "orp__summary-sub orp__mono", children: d.id }) : re && w ? /* @__PURE__ */ a("p", { className: "orp__summary-sub", children: [
192
+ n.now,
193
+ " ",
194
+ /* @__PURE__ */ o("span", { className: "orp__mono", children: w })
195
+ ] }) : c ? /* @__PURE__ */ o("p", { className: "orp__summary-sub", children: n.loading }) : I ? /* @__PURE__ */ o("p", { className: "orp__summary-sub orp__summary-sub--error", children: n.loadError }) : null
196
+ ] }),
197
+ /* @__PURE__ */ a("div", { className: "orp__summary-right", children: [
198
+ d && /* @__PURE__ */ o(oe, { model: d, t: n }),
199
+ /* @__PURE__ */ o("svg", { className: "orp__chevron", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ o("polyline", { points: "6,9 12,15 18,9" }) })
200
+ ] })
201
+ ] })
202
+ }
203
+ ),
204
+ m && /* @__PURE__ */ a("div", { className: "orp__popover", children: [
205
+ /* @__PURE__ */ a("div", { className: "orp__toolbar", children: [
206
+ /* @__PURE__ */ a("div", { className: "orp__search", children: [
207
+ /* @__PURE__ */ a("svg", { className: "orp__search-icon", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
208
+ /* @__PURE__ */ o("circle", { cx: "11", cy: "11", r: "8" }),
209
+ /* @__PURE__ */ o("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
210
+ ] }),
211
+ /* @__PURE__ */ o(
212
+ "input",
213
+ {
214
+ ref: Y,
215
+ className: "orp__input",
216
+ value: O,
217
+ onChange: (t) => Z(t.target.value),
218
+ onKeyDown: ce,
219
+ placeholder: n.searchPlaceholder,
220
+ "aria-label": n.searchPlaceholder,
221
+ role: "combobox",
222
+ "aria-expanded": "true",
223
+ "aria-controls": `${L ?? "orp"}-listbox`
224
+ }
225
+ )
226
+ ] }),
227
+ /* @__PURE__ */ a("div", { className: "orp__filters", children: [
228
+ /* @__PURE__ */ a("select", { className: "orp__select", value: S, onChange: (t) => ae(t.target.value), "aria-label": n.allVendors, children: [
229
+ /* @__PURE__ */ a("option", { value: "all", children: [
230
+ n.allVendors,
231
+ " (",
232
+ y.length,
233
+ ")"
234
+ ] }),
235
+ le.map(([t, i]) => /* @__PURE__ */ a("option", { value: t, children: [
236
+ H(t, M),
237
+ " (",
238
+ i,
239
+ ")"
240
+ ] }, t))
241
+ ] }),
242
+ /* @__PURE__ */ o("div", { className: "orp__sorts", role: "group", children: [["name", n.sortName], ["cheapest", n.sortCheapest], ["newest", n.sortNewest], ["context", n.sortContext]].map(([t, i]) => /* @__PURE__ */ o(
243
+ "button",
244
+ {
245
+ type: "button",
246
+ onClick: () => ie(t),
247
+ className: `orp__sort${j === t ? " orp__sort--active" : ""}`,
248
+ "aria-pressed": j === t,
249
+ children: i
250
+ },
251
+ t
252
+ )) }),
253
+ /* @__PURE__ */ o("span", { className: "orp__count", children: n.countOf(C.length, y.length) }),
254
+ u && /* @__PURE__ */ o("button", { type: "button", className: "orp__refresh", onClick: u, disabled: !!p, title: n.refresh, children: p ? n.refreshing : n.refresh })
255
+ ] })
256
+ ] }),
257
+ /* @__PURE__ */ a("div", { ref: X, className: "orp__list", role: "listbox", id: `${L ?? "orp"}-listbox`, children: [
258
+ z && /* @__PURE__ */ a(
259
+ "button",
260
+ {
261
+ type: "button",
262
+ role: "option",
263
+ "aria-selected": !s,
264
+ className: `orp__auto${s ? "" : " orp__auto--selected"}`,
265
+ onClick: () => {
266
+ l(""), _(!1);
267
+ },
268
+ children: [
269
+ n.auto,
270
+ w && /* @__PURE__ */ o("span", { className: "orp__auto-id orp__mono", children: w })
271
+ ]
272
+ }
273
+ ),
274
+ c && !r.length && /* @__PURE__ */ o("p", { className: "orp__message", children: n.loading }),
275
+ I && !r.length && /* @__PURE__ */ a("p", { className: "orp__message orp__message--error", children: [
276
+ n.loadError,
277
+ ": ",
278
+ I
279
+ ] }),
280
+ !c && !I && !C.length && /* @__PURE__ */ o("p", { className: "orp__message", children: n.noMatches(O) }),
281
+ C.map((t, i) => /* @__PURE__ */ o(
282
+ ge,
283
+ {
284
+ model: t,
285
+ index: i,
286
+ active: i === F,
287
+ selected: t.id === s,
288
+ isDefault: t.id === w,
289
+ t: n,
290
+ onHover: () => E(i),
291
+ onPick: () => ee(t.id)
292
+ },
293
+ t.id
294
+ ))
295
+ ] })
296
+ ] })
297
+ ] }),
298
+ d && !D && /* @__PURE__ */ a("div", { className: "orp__details", children: [
299
+ /* @__PURE__ */ o("span", { className: "orp__chip", children: H(d.vendor, M) }),
300
+ /* @__PURE__ */ a("span", { className: "orp__chip", children: [
301
+ n.context,
302
+ " ",
303
+ G(d.contextLength)
304
+ ] }),
305
+ d.maxCompletionTokens && /* @__PURE__ */ a("span", { className: "orp__chip", children: [
306
+ n.maxOutput,
307
+ " ",
308
+ G(d.maxCompletionTokens)
309
+ ] }),
310
+ d.builtInWebSearch && /* @__PURE__ */ o("span", { className: "orp__chip orp__chip--info", children: n.webTitle }),
311
+ d.supportsJsonMode && /* @__PURE__ */ o("span", { className: "orp__chip orp__chip--ok", children: n.json }),
312
+ d.supportsReasoning && /* @__PURE__ */ o("span", { className: "orp__chip orp__chip--violet", children: n.reasoning }),
313
+ d.pricing.perRequest > 0 && /* @__PURE__ */ a("span", { className: "orp__chip orp__chip--warn", children: [
314
+ "+",
315
+ Q(d.pricing.perRequest),
316
+ " ",
317
+ n.perRequest
318
+ ] }),
319
+ d.pricing.isFree && /* @__PURE__ */ o("span", { className: "orp__chip orp__chip--ok", children: n.free })
320
+ ] })
321
+ ] });
322
+ }
323
+ function ge({ model: e, index: r, active: s, selected: l, isDefault: c, t: p, onHover: g, onPick: u }) {
324
+ return /* @__PURE__ */ a(
325
+ "button",
326
+ {
327
+ type: "button",
328
+ role: "option",
329
+ "aria-selected": l,
330
+ "data-index": r,
331
+ "data-model-id": e.id,
332
+ onMouseEnter: g,
333
+ onClick: u,
334
+ className: `orp__row${s ? " orp__row--active" : ""}${l ? " orp__row--selected" : ""}`,
335
+ children: [
336
+ /* @__PURE__ */ a("div", { className: "orp__row-main", children: [
337
+ /* @__PURE__ */ a("p", { className: "orp__row-name", children: [
338
+ e.name,
339
+ c && /* @__PURE__ */ o("span", { className: "orp__row-default", children: p.isDefault })
340
+ ] }),
341
+ /* @__PURE__ */ o("p", { className: "orp__row-id", children: e.id })
342
+ ] }),
343
+ /* @__PURE__ */ a("div", { className: "orp__row-side", children: [
344
+ e.builtInWebSearch && /* @__PURE__ */ o("span", { className: "orp__badge orp__badge--info", title: p.webTitle, children: p.web }),
345
+ e.supportsJsonMode && /* @__PURE__ */ o("span", { className: "orp__badge orp__badge--ok", title: p.jsonTitle, children: p.json }),
346
+ /* @__PURE__ */ o("span", { className: "orp__ctx", children: G(e.contextLength) }),
347
+ /* @__PURE__ */ o(oe, { model: e, t: p })
348
+ ] })
349
+ ]
350
+ }
351
+ );
352
+ }
353
+ function oe({ model: e, t: r }) {
354
+ return e.pricing.isFree ? /* @__PURE__ */ o("span", { className: "orp__badge orp__badge--ok", children: r.free }) : /* @__PURE__ */ a("span", { className: "orp__price", title: r.priceTitle, children: [
355
+ Q(e.pricing.promptPerM),
356
+ " ",
357
+ /* @__PURE__ */ o("span", { className: "orp__price-sep", children: "/" }),
358
+ " ",
359
+ Q(e.pricing.completionPerM)
360
+ ] });
361
+ }
362
+ const se = "https://openrouter.ai/api/v1/models", Ne = 1e6;
363
+ function B(e) {
364
+ if (e == null || e === "") return null;
365
+ const r = typeof e == "number" ? e : Number(String(e));
366
+ return Number.isFinite(r) ? r : null;
367
+ }
368
+ function V(e) {
369
+ const r = B(e);
370
+ return r == null ? null : r * Ne;
371
+ }
372
+ function we(e) {
373
+ const r = e ?? {}, s = V(r.prompt) ?? 0, l = V(r.completion) ?? 0, c = B(r.request) ?? 0;
374
+ return {
375
+ promptPerM: s,
376
+ completionPerM: l,
377
+ reasoningPerM: V(r.internal_reasoning),
378
+ perRequest: c,
379
+ perWebSearch: B(r.web_search) ?? 0,
380
+ cacheReadPerM: V(r.input_cache_read),
381
+ perImage: B(r.image) ?? 0,
382
+ isFree: s === 0 && l === 0 && c === 0
383
+ };
384
+ }
385
+ function be(e) {
386
+ const r = e;
387
+ if (!r || typeof r.id != "string") return null;
388
+ const s = Array.isArray(r.architecture?.output_modalities) ? r.architecture.output_modalities : ["text"];
389
+ if (!s.includes("text")) return null;
390
+ const l = Array.isArray(r.supported_parameters) ? r.supported_parameters : [], c = r.id.indexOf("/"), p = c > 0 ? r.id.slice(0, c) : "other";
391
+ return {
392
+ id: r.id,
393
+ name: typeof r.name == "string" && r.name ? r.name : r.id,
394
+ description: typeof r.description == "string" ? r.description : "",
395
+ vendor: p,
396
+ created: typeof r.created == "number" ? r.created : 0,
397
+ contextLength: typeof r.context_length == "number" ? r.context_length : r.top_provider?.context_length ?? null,
398
+ maxCompletionTokens: typeof r.top_provider?.max_completion_tokens == "number" ? r.top_provider.max_completion_tokens : null,
399
+ inputModalities: Array.isArray(r.architecture?.input_modalities) ? r.architecture.input_modalities : ["text"],
400
+ outputModalities: s,
401
+ supportedParameters: l,
402
+ // Only Perplexity always searches without extra parameters; everything else needs the OpenRouter web plugin.
403
+ builtInWebSearch: p === "perplexity",
404
+ supportsJsonMode: l.includes("response_format") || l.includes("structured_outputs"),
405
+ supportsReasoning: l.includes("reasoning") || l.includes("include_reasoning"),
406
+ pricing: we(r.pricing)
407
+ };
408
+ }
409
+ function ye(e) {
410
+ const r = e?.data;
411
+ return Array.isArray(r) ? r.map(be).filter((s) => !!s) : [];
412
+ }
413
+ async function xe(e = {}) {
414
+ const r = { Accept: "application/json", ...e.headers ?? {} };
415
+ e.apiKey && (r.Authorization = `Bearer ${e.apiKey}`);
416
+ const s = await fetch(e.url ?? se, { headers: r, signal: e.signal });
417
+ if (!s.ok) throw new Error(`OpenRouter /models returned ${s.status}`);
418
+ return ye(await s.json());
419
+ }
420
+ const Me = 600 * 1e3, P = /* @__PURE__ */ new Map(), J = /* @__PURE__ */ new Map();
421
+ function $e() {
422
+ P.clear();
423
+ }
424
+ function Ae(e = {}) {
425
+ const { fetcher: r, ttlMs: s = Me, enabled: l = !0, apiKey: c, url: p = se, headers: g } = e, u = r ? `fetcher:${p}` : `${p}|${c ?? ""}`, [R, w] = N(() => P.get(u) ?? null), [z, x] = N(!1), [D, q] = N(!1), [M, v] = N(null), b = T(!0), $ = T(r);
426
+ $.current = r, k(() => (b.current = !0, () => {
427
+ b.current = !1;
428
+ }), []);
429
+ const A = ue(async (L) => {
430
+ const n = P.get(u);
431
+ if (!L && n && Date.now() - n.fetchedAt < s) {
432
+ w(n);
433
+ return;
434
+ }
435
+ let m = J.get(u);
436
+ m || (m = ($.current ? $.current() : xe({ apiKey: c, url: p, headers: g })).then((_) => (P.set(u, { models: _, fetchedAt: Date.now() }), _)).finally(() => J.delete(u)), J.set(u, m)), n ? q(!0) : x(!0), v(null);
437
+ try {
438
+ await m, b.current && w(P.get(u) ?? null);
439
+ } catch (_) {
440
+ b.current && v(_ instanceof Error ? _ : new Error(String(_)));
441
+ } finally {
442
+ b.current && (x(!1), q(!1));
443
+ }
444
+ }, [u, s, c, p, g]);
445
+ return k(() => {
446
+ l && A(!1);
447
+ }, [l, A]), {
448
+ models: R?.models ?? [],
449
+ loading: z,
450
+ refreshing: D,
451
+ error: M,
452
+ fetchedAt: R ? new Date(R.fetchedAt).toISOString() : null,
453
+ refresh: () => A(!0)
454
+ };
455
+ }
456
+ export {
457
+ me as DEFAULT_VENDOR_LABELS,
458
+ se as OPENROUTER_MODELS_URL,
459
+ Re as OpenRouterModelPicker,
460
+ ne as blendedPricePerM,
461
+ $e as clearOpenRouterModelsCache,
462
+ he as enLabels,
463
+ xe as fetchOpenRouterModels,
464
+ G as formatContext,
465
+ Q as formatPerM,
466
+ be as normalizeModel,
467
+ ye as normalizeModelsResponse,
468
+ we as normalizePricing,
469
+ ke as plLabels,
470
+ Ae as useOpenRouterModels,
471
+ H as vendorLabel
472
+ };
473
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/format.ts","../src/labels.ts","../src/OpenRouterModelPicker.tsx","../src/models.ts","../src/useOpenRouterModels.ts"],"sourcesContent":["import type { OpenRouterModel } from './models';\n\n/** \"$1.25\" / \"$0.0004\" / \"$0\" — USD per 1M tokens. */\nexport function formatPerM(value: number | null | undefined): string {\n if (value == null) return '—';\n if (value === 0) return '$0';\n if (value < 0.01) return `$${value.toFixed(4)}`;\n return `$${value.toFixed(2)}`;\n}\n\n/** \"200k\" / \"1M\" / \"1.5M\" context window. */\nexport function formatContext(tokens: number | null | undefined): string {\n if (!tokens) return '—';\n if (tokens >= 1_000_000) {\n const m = (tokens / 1_000_000).toFixed(1);\n return `${m.endsWith('.0') ? m.slice(0, -2) : m}M`;\n }\n if (tokens >= 1000) return `${Math.round(tokens / 1000)}k`;\n return String(tokens);\n}\n\nexport const DEFAULT_VENDOR_LABELS: Record<string, string> = {\n openai: 'OpenAI', anthropic: 'Anthropic', google: 'Google', 'meta-llama': 'Meta', mistralai: 'Mistral',\n perplexity: 'Perplexity', 'x-ai': 'xAI', deepseek: 'DeepSeek', qwen: 'Qwen', cohere: 'Cohere',\n amazon: 'Amazon', microsoft: 'Microsoft', nvidia: 'NVIDIA', moonshotai: 'Moonshot', 'z-ai': 'Z.ai',\n openrouter: 'OpenRouter',\n};\n\nexport function vendorLabel(vendor: string, overrides?: Record<string, string>): string {\n return overrides?.[vendor] ?? DEFAULT_VENDOR_LABELS[vendor]\n ?? vendor.replace(/-/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Blended USD per 1M tokens at a typical 3:1 input:output ratio — used by the\n * \"cheapest\" sort. A per-request fee is counted as ~1000 requests per 1M tokens.\n */\nexport function blendedPricePerM(m: OpenRouterModel): number {\n return m.pricing.promptPerM * 0.75 + m.pricing.completionPerM * 0.25 + m.pricing.perRequest * 1000;\n}\n","export interface PickerLabels {\n placeholder: string;\n searchPlaceholder: string;\n allVendors: string;\n sortName: string;\n sortCheapest: string;\n sortNewest: string;\n sortContext: string;\n refresh: string;\n refreshing: string;\n loading: string;\n loadError: string;\n noMatches: (query: string) => string;\n countOf: (shown: number, total: number) => string;\n modelsCount: (n: number) => string;\n pricesFrom: (time: string) => string;\n auto: string;\n now: string;\n isDefault: string;\n free: string;\n web: string;\n json: string;\n reasoning: string;\n context: string;\n maxOutput: string;\n perRequest: string;\n priceTitle: string;\n webTitle: string;\n jsonTitle: string;\n}\n\nexport const enLabels: PickerLabels = {\n placeholder: 'Select a model…',\n searchPlaceholder: 'Search: gpt-5, claude, sonar, gemini, llama…',\n allVendors: 'All vendors',\n sortName: 'Name',\n sortCheapest: 'Cheapest',\n sortNewest: 'Newest',\n sortContext: 'Context',\n refresh: 'Refresh prices',\n refreshing: 'refreshing…',\n loading: 'Loading models from OpenRouter…',\n loadError: 'Could not load the model list',\n noMatches: (q) => `No models match “${q}”.`,\n countOf: (shown, total) => `${shown} of ${total}`,\n modelsCount: (n) => `${n} OpenRouter models`,\n pricesFrom: (t) => `prices as of ${t}`,\n auto: 'Automatic (server default)',\n now: 'now:',\n isDefault: 'default',\n free: 'free',\n web: 'web',\n json: 'json',\n reasoning: 'reasoning',\n context: 'context',\n maxOutput: 'max output',\n perRequest: 'per request',\n priceTitle: 'USD per 1M tokens: input / output',\n webTitle: 'Built-in web search',\n jsonTitle: 'Supports response_format JSON',\n};\n\nexport const plLabels: PickerLabels = {\n placeholder: 'Wybierz model…',\n searchPlaceholder: 'Szukaj: gpt-5, claude, sonar, gemini, llama…',\n allVendors: 'Wszyscy producenci',\n sortName: 'Nazwa',\n sortCheapest: 'Najtańsze',\n sortNewest: 'Najnowsze',\n sortContext: 'Kontekst',\n refresh: 'Odśwież ceny',\n refreshing: 'odświeżam…',\n loading: 'Pobieram listę modeli z OpenRoutera…',\n loadError: 'Nie udało się pobrać listy modeli',\n noMatches: (q) => `Brak modeli pasujących do „${q}”.`,\n countOf: (shown, total) => `${shown} z ${total}`,\n modelsCount: (n) => `${n} modeli OpenRouter`,\n pricesFrom: (t) => `ceny z ${t}`,\n auto: 'Automatycznie (domyślny z serwera)',\n now: 'teraz:',\n isDefault: 'domyślny',\n free: 'darmowy',\n web: 'web',\n json: 'json',\n reasoning: 'reasoning',\n context: 'kontekst',\n maxOutput: 'max wyjście',\n perRequest: 'za żądanie',\n priceTitle: 'USD za 1M tokenów: wejście / wyjście',\n webTitle: 'Wbudowane wyszukiwanie w sieci',\n jsonTitle: 'Obsługuje response_format JSON',\n};\n","import { useEffect, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport type { OpenRouterModel } from './models';\nimport { blendedPricePerM, formatContext, formatPerM, vendorLabel } from './format';\nimport { enLabels, type PickerLabels } from './labels';\n\nexport type PickerSortKey = 'name' | 'cheapest' | 'newest' | 'context';\n\nexport interface OpenRouterModelPickerProps {\n /** Normalized model list — from `useOpenRouterModels`, your own query, or a server. */\n models: OpenRouterModel[];\n /** Selected model id. Empty/undefined = nothing selected (or \"automatic\" when `allowAuto`). */\n value?: string;\n /** Called with the model id, or \"\" when the automatic option is chosen. */\n onChange: (id: string) => void;\n loading?: boolean;\n refreshing?: boolean;\n error?: unknown;\n /** Shown as a \"Refresh prices\" button inside the dropdown. */\n onRefresh?: () => void;\n /** ISO string or Date of the list currently shown — rendered next to the label. */\n fetchedAt?: string | Date | null;\n /** Model the server would use when none is chosen; marked in the list and shown under \"automatic\". */\n defaultModelId?: string;\n /** Adds an \"automatic\" entry at the top of the list that maps to value \"\". */\n allowAuto?: boolean;\n /** Label above the control. Omit for a bare control. */\n label?: ReactNode;\n /** Tighter paddings, no detail chips under the control. */\n compact?: boolean;\n disabled?: boolean;\n /** Partial overrides merged over the English defaults; import `plLabels` for Polish. */\n labels?: Partial<PickerLabels>;\n /** Vendor id → display name overrides (e.g. { 'x-ai': 'xAI' }). */\n vendorLabels?: Record<string, string>;\n /** Restrict the list (e.g. only models with JSON mode). */\n filter?: (model: OpenRouterModel) => boolean;\n initialSort?: PickerSortKey;\n /** Force light/dark regardless of the OS setting. */\n theme?: 'light' | 'dark';\n className?: string;\n id?: string;\n}\n\nfunction matches(m: OpenRouterModel, q: string, vendors?: Record<string, string>): boolean {\n if (!q) return true;\n const hay = `${m.id} ${m.name} ${vendorLabel(m.vendor, vendors)}`.toLowerCase();\n return q.split(/\\s+/).every((term) => hay.includes(term));\n}\n\nfunction timeLabel(value: string | Date | null | undefined): string | null {\n if (!value) return null;\n const d = typeof value === 'string' ? new Date(value) : value;\n return Number.isNaN(d.getTime()) ? null : d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n}\n\n/**\n * Searchable picker over the full OpenRouter model list: text search across\n * id / name / vendor, vendor filter, sorting, keyboard navigation, and a price\n * per 1M tokens on every row — choosing a model is choosing its price list.\n */\nexport function OpenRouterModelPicker(props: OpenRouterModelPickerProps) {\n const {\n models, value, onChange, loading, refreshing, error, onRefresh, fetchedAt, defaultModelId,\n allowAuto, label, compact, disabled, vendorLabels, filter, initialSort = 'name', theme, className, id,\n } = props;\n const t: PickerLabels = useMemo(() => ({ ...enLabels, ...(props.labels ?? {}) }), [props.labels]);\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [vendor, setVendor] = useState('all');\n const [sort, setSort] = useState<PickerSortKey>(initialSort);\n const [cursor, setCursor] = useState(0);\n const rootRef = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const visible = useMemo(() => (filter ? models.filter(filter) : models), [models, filter]);\n const selected = value ? visible.find((m) => m.id === value) ?? models.find((m) => m.id === value) : undefined;\n\n const vendors = useMemo(() => {\n const counts = new Map<string, number>();\n for (const m of visible) counts.set(m.vendor, (counts.get(m.vendor) ?? 0) + 1);\n return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));\n }, [visible]);\n\n const filtered = useMemo(() => {\n const q = query.trim().toLowerCase();\n const list = visible.filter((m) => (vendor === 'all' || m.vendor === vendor) && matches(m, q, vendorLabels));\n const sorters: Record<PickerSortKey, (a: OpenRouterModel, b: OpenRouterModel) => number> = {\n name: (a, b) => a.name.localeCompare(b.name),\n cheapest: (a, b) => blendedPricePerM(a) - blendedPricePerM(b) || a.name.localeCompare(b.name),\n newest: (a, b) => b.created - a.created || a.name.localeCompare(b.name),\n context: (a, b) => (b.contextLength ?? 0) - (a.contextLength ?? 0) || a.name.localeCompare(b.name),\n };\n return [...list].sort(sorters[sort]);\n }, [visible, query, vendor, sort, vendorLabels]);\n\n // Close on outside click.\n useEffect(() => {\n if (!open) return;\n const onDown = (e: MouseEvent) => {\n if (rootRef.current && !rootRef.current.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener('mousedown', onDown);\n return () => document.removeEventListener('mousedown', onDown);\n }, [open]);\n\n useEffect(() => { setCursor(0); }, [query, vendor, sort]);\n\n useEffect(() => {\n if (!open) return;\n inputRef.current?.focus();\n const idx = filtered.findIndex((m) => m.id === value);\n if (idx >= 0) setCursor(idx);\n // Only when opening: re-running on every filter change would fight the cursor reset above.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n useEffect(() => {\n const el = listRef.current?.querySelector<HTMLElement>(`[data-index=\"${cursor}\"]`);\n el?.scrollIntoView?.({ block: 'nearest' });\n }, [cursor, open]);\n\n const pick = (modelId: string) => { onChange(modelId); setOpen(false); setQuery(''); };\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'ArrowDown') { e.preventDefault(); setCursor((c) => Math.min(filtered.length - 1, c + 1)); }\n else if (e.key === 'ArrowUp') { e.preventDefault(); setCursor((c) => Math.max(0, c - 1)); }\n else if (e.key === 'Enter') { e.preventDefault(); const m = filtered[cursor]; if (m) pick(m.id); }\n else if (e.key === 'Escape') { setOpen(false); }\n };\n\n const isAuto = !!allowAuto && !value;\n const summaryTitle = selected ? selected.name : value ? value : isAuto ? t.auto : t.placeholder;\n const priceTime = timeLabel(fetchedAt);\n const errorMessage = error ? (error instanceof Error ? error.message : String(error)) : null;\n\n const rootClass = ['orp', open && 'orp--open', compact && 'orp--compact', label && 'orp--has-label', className]\n .filter(Boolean).join(' ');\n\n return (\n <div ref={rootRef} className={rootClass} data-theme={theme} id={id}>\n {label && (\n <div className=\"orp__label\">\n <span>{label}</span>\n <span className=\"orp__label-meta\">\n {models.length ? t.modelsCount(models.length) : ''}\n {priceTime && ` · ${t.pricesFrom(priceTime)}`}\n </span>\n </div>\n )}\n\n <div className=\"orp__anchor\">\n <button\n type=\"button\"\n className=\"orp__summary\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n onClick={() => setOpen((o) => !o)}\n >\n <div className=\"orp__summary-inner\">\n <div className=\"orp__summary-text\">\n <p className=\"orp__summary-title\">{summaryTitle}</p>\n {selected ? (\n <p className=\"orp__summary-sub orp__mono\">{selected.id}</p>\n ) : isAuto && defaultModelId ? (\n <p className=\"orp__summary-sub\">{t.now} <span className=\"orp__mono\">{defaultModelId}</span></p>\n ) : loading ? (\n <p className=\"orp__summary-sub\">{t.loading}</p>\n ) : errorMessage ? (\n <p className=\"orp__summary-sub orp__summary-sub--error\">{t.loadError}</p>\n ) : null}\n </div>\n <div className=\"orp__summary-right\">\n {selected && <PriceTag model={selected} t={t} />}\n <svg className=\"orp__chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <polyline points=\"6,9 12,15 18,9\" />\n </svg>\n </div>\n </div>\n </button>\n\n {open && (\n <div className=\"orp__popover\">\n <div className=\"orp__toolbar\">\n <div className=\"orp__search\">\n <svg className=\"orp__search-icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" /><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\" />\n </svg>\n <input\n ref={inputRef}\n className=\"orp__input\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={onKeyDown}\n placeholder={t.searchPlaceholder}\n aria-label={t.searchPlaceholder}\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={`${id ?? 'orp'}-listbox`}\n />\n </div>\n <div className=\"orp__filters\">\n <select className=\"orp__select\" value={vendor} onChange={(e) => setVendor(e.target.value)} aria-label={t.allVendors}>\n <option value=\"all\">{t.allVendors} ({visible.length})</option>\n {vendors.map(([v, n]) => <option key={v} value={v}>{vendorLabel(v, vendorLabels)} ({n})</option>)}\n </select>\n <div className=\"orp__sorts\" role=\"group\">\n {([['name', t.sortName], ['cheapest', t.sortCheapest], ['newest', t.sortNewest], ['context', t.sortContext]] as const).map(([key, text]) => (\n <button key={key} type=\"button\" onClick={() => setSort(key)}\n className={`orp__sort${sort === key ? ' orp__sort--active' : ''}`} aria-pressed={sort === key}>\n {text}\n </button>\n ))}\n </div>\n <span className=\"orp__count\">{t.countOf(filtered.length, visible.length)}</span>\n {onRefresh && (\n <button type=\"button\" className=\"orp__refresh\" onClick={onRefresh} disabled={!!refreshing} title={t.refresh}>\n {refreshing ? t.refreshing : t.refresh}\n </button>\n )}\n </div>\n </div>\n\n <div ref={listRef} className=\"orp__list\" role=\"listbox\" id={`${id ?? 'orp'}-listbox`}>\n {allowAuto && (\n <button type=\"button\" role=\"option\" aria-selected={!value}\n className={`orp__auto${!value ? ' orp__auto--selected' : ''}`}\n onClick={() => { onChange(''); setOpen(false); }}>\n {t.auto}\n {defaultModelId && <span className=\"orp__auto-id orp__mono\">{defaultModelId}</span>}\n </button>\n )}\n {loading && !models.length && <p className=\"orp__message\">{t.loading}</p>}\n {errorMessage && !models.length && <p className=\"orp__message orp__message--error\">{t.loadError}: {errorMessage}</p>}\n {!loading && !errorMessage && !filtered.length && <p className=\"orp__message\">{t.noMatches(query)}</p>}\n {filtered.map((m, i) => (\n <ModelRow key={m.id} model={m} index={i} active={i === cursor} selected={m.id === value}\n isDefault={m.id === defaultModelId} t={t} onHover={() => setCursor(i)} onPick={() => pick(m.id)} />\n ))}\n </div>\n </div>\n )}\n </div>\n\n {selected && !compact && (\n <div className=\"orp__details\">\n <span className=\"orp__chip\">{vendorLabel(selected.vendor, vendorLabels)}</span>\n <span className=\"orp__chip\">{t.context} {formatContext(selected.contextLength)}</span>\n {selected.maxCompletionTokens && <span className=\"orp__chip\">{t.maxOutput} {formatContext(selected.maxCompletionTokens)}</span>}\n {selected.builtInWebSearch && <span className=\"orp__chip orp__chip--info\">{t.webTitle}</span>}\n {selected.supportsJsonMode && <span className=\"orp__chip orp__chip--ok\">{t.json}</span>}\n {selected.supportsReasoning && <span className=\"orp__chip orp__chip--violet\">{t.reasoning}</span>}\n {selected.pricing.perRequest > 0 && <span className=\"orp__chip orp__chip--warn\">+{formatPerM(selected.pricing.perRequest)} {t.perRequest}</span>}\n {selected.pricing.isFree && <span className=\"orp__chip orp__chip--ok\">{t.free}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction ModelRow({ model, index, active, selected, isDefault, t, onHover, onPick }: {\n model: OpenRouterModel; index: number; active: boolean; selected: boolean; isDefault: boolean;\n t: PickerLabels; onHover: () => void; onPick: () => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={selected}\n data-index={index}\n data-model-id={model.id}\n onMouseEnter={onHover}\n onClick={onPick}\n className={`orp__row${active ? ' orp__row--active' : ''}${selected ? ' orp__row--selected' : ''}`}\n >\n <div className=\"orp__row-main\">\n <p className=\"orp__row-name\">\n {model.name}\n {isDefault && <span className=\"orp__row-default\">{t.isDefault}</span>}\n </p>\n <p className=\"orp__row-id\">{model.id}</p>\n </div>\n <div className=\"orp__row-side\">\n {model.builtInWebSearch && <span className=\"orp__badge orp__badge--info\" title={t.webTitle}>{t.web}</span>}\n {model.supportsJsonMode && <span className=\"orp__badge orp__badge--ok\" title={t.jsonTitle}>{t.json}</span>}\n <span className=\"orp__ctx\">{formatContext(model.contextLength)}</span>\n <PriceTag model={model} t={t} />\n </div>\n </button>\n );\n}\n\n/** \"$1.25 / $10.00\" — USD per 1M tokens, input / output. */\nfunction PriceTag({ model, t }: { model: OpenRouterModel; t: PickerLabels }) {\n if (model.pricing.isFree) return <span className=\"orp__badge orp__badge--ok\">{t.free}</span>;\n return (\n <span className=\"orp__price\" title={t.priceTitle}>\n {formatPerM(model.pricing.promptPerM)} <span className=\"orp__price-sep\">/</span> {formatPerM(model.pricing.completionPerM)}\n </span>\n );\n}\n","/**\n * Normalization of the OpenRouter `GET /api/v1/models` response.\n *\n * Pure TypeScript, no React — safe to reuse on a server. OpenRouter reports\n * prices as STRINGS per single token (e.g. \"0.000008\"); here they become\n * numbers per 1,000,000 tokens, which is how price lists are usually read.\n */\n\nexport const OPENROUTER_MODELS_URL = 'https://openrouter.ai/api/v1/models';\n\nexport interface ModelPricing {\n /** USD per 1M input (prompt) tokens. */\n promptPerM: number;\n /** USD per 1M output (completion) tokens. */\n completionPerM: number;\n /** USD per 1M reasoning tokens, when the provider bills them separately. */\n reasoningPerM: number | null;\n /** Flat USD fee per request (some providers charge it on top of tokens). */\n perRequest: number;\n /** USD per web search performed by a model with built-in search. */\n perWebSearch: number;\n /** USD per 1M tokens read from prompt cache. */\n cacheReadPerM: number | null;\n /** USD per input image. */\n perImage: number;\n /** True when prompt, completion and per-request prices are all zero. */\n isFree: boolean;\n}\n\nexport interface OpenRouterModel {\n /** OpenRouter slug, e.g. \"openai/gpt-5\". */\n id: string;\n /** Display name, e.g. \"OpenAI: GPT-5\". */\n name: string;\n description: string;\n /** Vendor taken from the id prefix (\"openai/gpt-5\" → \"openai\"). */\n vendor: string;\n /** Unix timestamp (seconds) when the model was added. */\n created: number;\n contextLength: number | null;\n maxCompletionTokens: number | null;\n inputModalities: string[];\n outputModalities: string[];\n /** OpenAI-compatible parameters the model accepts (union over providers). */\n supportedParameters: string[];\n /** The model searches the web on its own (Perplexity) — no plugin needed. */\n builtInWebSearch: boolean;\n /** Accepts `response_format` / `structured_outputs`. */\n supportsJsonMode: boolean;\n supportsReasoning: boolean;\n pricing: ModelPricing;\n}\n\nconst PER_M = 1_000_000;\n\nfunction perToken(value: unknown): number | null {\n if (value == null || value === '') return null;\n const n = typeof value === 'number' ? value : Number(String(value));\n return Number.isFinite(n) ? n : null;\n}\n\nfunction perMillion(value: unknown): number | null {\n const n = perToken(value);\n return n == null ? null : n * PER_M;\n}\n\n/** Raw `pricing` object of one model → per-1M prices. */\nexport function normalizePricing(raw: unknown): ModelPricing {\n const r = (raw ?? {}) as Record<string, unknown>;\n const promptPerM = perMillion(r.prompt) ?? 0;\n const completionPerM = perMillion(r.completion) ?? 0;\n const perRequest = perToken(r.request) ?? 0;\n return {\n promptPerM,\n completionPerM,\n reasoningPerM: perMillion(r.internal_reasoning),\n perRequest,\n perWebSearch: perToken(r.web_search) ?? 0,\n cacheReadPerM: perMillion(r.input_cache_read),\n perImage: perToken(r.image) ?? 0,\n isFree: promptPerM === 0 && completionPerM === 0 && perRequest === 0,\n };\n}\n\n/**\n * One raw entry from `data[]` → `OpenRouterModel`.\n * Returns null for entries that cannot produce text (image-only models).\n */\nexport function normalizeModel(raw: unknown): OpenRouterModel | null {\n const r = raw as Record<string, any> | null;\n if (!r || typeof r.id !== 'string') return null;\n\n const outputModalities: string[] = Array.isArray(r.architecture?.output_modalities)\n ? r.architecture.output_modalities\n : ['text'];\n if (!outputModalities.includes('text')) return null;\n\n const supportedParameters: string[] = Array.isArray(r.supported_parameters) ? r.supported_parameters : [];\n const slash = r.id.indexOf('/');\n const vendor = slash > 0 ? r.id.slice(0, slash) : 'other';\n\n return {\n id: r.id,\n name: typeof r.name === 'string' && r.name ? r.name : r.id,\n description: typeof r.description === 'string' ? r.description : '',\n vendor,\n created: typeof r.created === 'number' ? r.created : 0,\n contextLength: typeof r.context_length === 'number' ? r.context_length : (r.top_provider?.context_length ?? null),\n maxCompletionTokens: typeof r.top_provider?.max_completion_tokens === 'number' ? r.top_provider.max_completion_tokens : null,\n inputModalities: Array.isArray(r.architecture?.input_modalities) ? r.architecture.input_modalities : ['text'],\n outputModalities,\n supportedParameters,\n // Only Perplexity always searches without extra parameters; everything else needs the OpenRouter web plugin.\n builtInWebSearch: vendor === 'perplexity',\n supportsJsonMode: supportedParameters.includes('response_format') || supportedParameters.includes('structured_outputs'),\n supportsReasoning: supportedParameters.includes('reasoning') || supportedParameters.includes('include_reasoning'),\n pricing: normalizePricing(r.pricing),\n };\n}\n\n/** Whole `/models` response body → normalized list (text-capable models only). */\nexport function normalizeModelsResponse(body: unknown): OpenRouterModel[] {\n const data = (body as { data?: unknown[] } | null)?.data;\n if (!Array.isArray(data)) return [];\n return data.map(normalizeModel).filter((m): m is OpenRouterModel => !!m);\n}\n\nexport interface FetchModelsOptions {\n /** Optional. Without a key the public list is returned; with one, only models the key can call. */\n apiKey?: string;\n url?: string;\n signal?: AbortSignal;\n /** Extra headers, e.g. `HTTP-Referer` / `X-Title` for OpenRouter app attribution. */\n headers?: Record<string, string>;\n}\n\n/** Fetch and normalize the OpenRouter model list. Works in browsers and Node 18+. */\nexport async function fetchOpenRouterModels(options: FetchModelsOptions = {}): Promise<OpenRouterModel[]> {\n const headers: Record<string, string> = { Accept: 'application/json', ...(options.headers ?? {}) };\n if (options.apiKey) headers.Authorization = `Bearer ${options.apiKey}`;\n const res = await fetch(options.url ?? OPENROUTER_MODELS_URL, { headers, signal: options.signal });\n if (!res.ok) throw new Error(`OpenRouter /models returned ${res.status}`);\n return normalizeModelsResponse(await res.json());\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { fetchOpenRouterModels, OPENROUTER_MODELS_URL, type FetchModelsOptions, type OpenRouterModel } from './models';\n\nexport interface UseOpenRouterModelsOptions extends Omit<FetchModelsOptions, 'signal'> {\n /**\n * Replace the default fetch entirely — e.g. call your own backend that proxies\n * OpenRouter and adds auth. Must resolve to already-normalized models\n * (use `normalizeModelsResponse` if your backend forwards the raw body).\n */\n fetcher?: () => Promise<OpenRouterModel[]>;\n /** How long a fetched list stays fresh across all hook instances. Default 10 minutes. */\n ttlMs?: number;\n /** Set to false to skip fetching (e.g. until the user opens the picker). */\n enabled?: boolean;\n}\n\nexport interface UseOpenRouterModelsResult {\n models: OpenRouterModel[];\n loading: boolean;\n refreshing: boolean;\n error: Error | null;\n /** ISO time of the list currently held (null before the first successful fetch). */\n fetchedAt: string | null;\n /** Bypass the cache and fetch again. */\n refresh: () => Promise<void>;\n}\n\ninterface CacheEntry {\n models: OpenRouterModel[];\n fetchedAt: number;\n}\n\nconst DEFAULT_TTL_MS = 10 * 60 * 1000;\nconst cache = new Map<string, CacheEntry>();\nconst inflight = new Map<string, Promise<OpenRouterModel[]>>();\n\n/** Clear the module-level cache (tests, or after changing the API key). */\nexport function clearOpenRouterModelsCache(): void {\n cache.clear();\n}\n\n/**\n * Loads the OpenRouter model list with a small module-level cache, so several\n * pickers on one page share a single request. No external state library needed.\n * If you already use TanStack Query or SWR, skip this hook and pass `models`\n * from your own query into the picker.\n */\nexport function useOpenRouterModels(options: UseOpenRouterModelsOptions = {}): UseOpenRouterModelsResult {\n const { fetcher, ttlMs = DEFAULT_TTL_MS, enabled = true, apiKey, url = OPENROUTER_MODELS_URL, headers } = options;\n const cacheKey = fetcher ? `fetcher:${url}` : `${url}|${apiKey ?? ''}`;\n\n const [entry, setEntry] = useState<CacheEntry | null>(() => cache.get(cacheKey) ?? null);\n const [loading, setLoading] = useState(false);\n const [refreshing, setRefreshing] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const mounted = useRef(true);\n const fetcherRef = useRef(fetcher);\n fetcherRef.current = fetcher;\n\n useEffect(() => {\n mounted.current = true;\n return () => { mounted.current = false; };\n }, []);\n\n const load = useCallback(async (force: boolean) => {\n const cached = cache.get(cacheKey);\n if (!force && cached && Date.now() - cached.fetchedAt < ttlMs) {\n setEntry(cached);\n return;\n }\n let promise = inflight.get(cacheKey);\n if (!promise) {\n promise = (fetcherRef.current ? fetcherRef.current() : fetchOpenRouterModels({ apiKey, url, headers }))\n .then((models) => {\n cache.set(cacheKey, { models, fetchedAt: Date.now() });\n return models;\n })\n .finally(() => inflight.delete(cacheKey));\n inflight.set(cacheKey, promise);\n }\n cached ? setRefreshing(true) : setLoading(true);\n setError(null);\n try {\n await promise;\n if (mounted.current) setEntry(cache.get(cacheKey) ?? null);\n } catch (e) {\n if (mounted.current) setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n if (mounted.current) { setLoading(false); setRefreshing(false); }\n }\n }, [cacheKey, ttlMs, apiKey, url, headers]);\n\n useEffect(() => {\n if (enabled) void load(false);\n }, [enabled, load]);\n\n return {\n models: entry?.models ?? [],\n loading,\n refreshing,\n error,\n fetchedAt: entry ? new Date(entry.fetchedAt).toISOString() : null,\n refresh: () => load(true),\n };\n}\n"],"names":["formatPerM","value","formatContext","tokens","m","DEFAULT_VENDOR_LABELS","vendorLabel","vendor","overrides","c","blendedPricePerM","enLabels","q","shown","total","n","t","plLabels","matches","vendors","hay","term","timeLabel","d","OpenRouterModelPicker","props","models","onChange","loading","refreshing","error","onRefresh","fetchedAt","defaultModelId","allowAuto","label","compact","disabled","vendorLabels","filter","initialSort","theme","className","id","useMemo","open","setOpen","useState","query","setQuery","setVendor","sort","setSort","cursor","setCursor","rootRef","useRef","listRef","inputRef","visible","selected","counts","a","b","filtered","list","sorters","useEffect","onDown","e","idx","pick","modelId","onKeyDown","isAuto","summaryTitle","priceTime","errorMessage","rootClass","jsxs","jsx","o","PriceTag","v","key","text","ModelRow","model","index","active","isDefault","onHover","onPick","OPENROUTER_MODELS_URL","PER_M","perToken","perMillion","normalizePricing","raw","promptPerM","completionPerM","perRequest","normalizeModel","outputModalities","supportedParameters","slash","normalizeModelsResponse","body","data","fetchOpenRouterModels","options","headers","res","DEFAULT_TTL_MS","cache","inflight","clearOpenRouterModelsCache","useOpenRouterModels","fetcher","ttlMs","enabled","apiKey","url","cacheKey","entry","setEntry","setLoading","setRefreshing","setError","mounted","fetcherRef","load","useCallback","force","cached","promise"],"mappings":"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"}