@aituber-onair/chat 0.16.0 → 0.18.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.
Files changed (45) hide show
  1. package/README.ja.md +31 -2
  2. package/README.md +30 -2
  3. package/dist/cjs/constants/gemini.d.ts +1 -0
  4. package/dist/cjs/constants/gemini.d.ts.map +1 -1
  5. package/dist/cjs/constants/gemini.js +3 -1
  6. package/dist/cjs/constants/gemini.js.map +1 -1
  7. package/dist/cjs/constants/openrouter.js +1 -1
  8. package/dist/cjs/constants/openrouter.js.map +1 -1
  9. package/dist/cjs/services/providers/gemini/GeminiChatServiceProvider.d.ts.map +1 -1
  10. package/dist/cjs/services/providers/gemini/GeminiChatServiceProvider.js +1 -0
  11. package/dist/cjs/services/providers/gemini/GeminiChatServiceProvider.js.map +1 -1
  12. package/dist/cjs/services/providers/openrouter/OpenRouterChatServiceProvider.d.ts.map +1 -1
  13. package/dist/cjs/services/providers/openrouter/OpenRouterChatServiceProvider.js +1 -1
  14. package/dist/cjs/services/providers/openrouter/OpenRouterChatServiceProvider.js.map +1 -1
  15. package/dist/cjs/utils/index.d.ts +1 -0
  16. package/dist/cjs/utils/index.d.ts.map +1 -1
  17. package/dist/cjs/utils/index.js +1 -0
  18. package/dist/cjs/utils/index.js.map +1 -1
  19. package/dist/cjs/utils/refreshOpenRouterFreeModels.d.ts +25 -0
  20. package/dist/cjs/utils/refreshOpenRouterFreeModels.d.ts.map +1 -0
  21. package/dist/cjs/utils/refreshOpenRouterFreeModels.js +207 -0
  22. package/dist/cjs/utils/refreshOpenRouterFreeModels.js.map +1 -0
  23. package/dist/esm/constants/gemini.d.ts +1 -0
  24. package/dist/esm/constants/gemini.d.ts.map +1 -1
  25. package/dist/esm/constants/gemini.js +2 -0
  26. package/dist/esm/constants/gemini.js.map +1 -1
  27. package/dist/esm/constants/openrouter.js +1 -1
  28. package/dist/esm/constants/openrouter.js.map +1 -1
  29. package/dist/esm/services/providers/gemini/GeminiChatServiceProvider.d.ts.map +1 -1
  30. package/dist/esm/services/providers/gemini/GeminiChatServiceProvider.js +2 -1
  31. package/dist/esm/services/providers/gemini/GeminiChatServiceProvider.js.map +1 -1
  32. package/dist/esm/services/providers/openrouter/OpenRouterChatServiceProvider.d.ts.map +1 -1
  33. package/dist/esm/services/providers/openrouter/OpenRouterChatServiceProvider.js +2 -2
  34. package/dist/esm/services/providers/openrouter/OpenRouterChatServiceProvider.js.map +1 -1
  35. package/dist/esm/utils/index.d.ts +1 -0
  36. package/dist/esm/utils/index.d.ts.map +1 -1
  37. package/dist/esm/utils/index.js +1 -0
  38. package/dist/esm/utils/index.js.map +1 -1
  39. package/dist/esm/utils/refreshOpenRouterFreeModels.d.ts +25 -0
  40. package/dist/esm/utils/refreshOpenRouterFreeModels.d.ts.map +1 -0
  41. package/dist/esm/utils/refreshOpenRouterFreeModels.js +204 -0
  42. package/dist/esm/utils/refreshOpenRouterFreeModels.js.map +1 -0
  43. package/dist/umd/aituber-onair-chat.js +213 -2
  44. package/dist/umd/aituber-onair-chat.min.js +6 -6
  45. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { MODEL_GPT_OSS_20B_FREE, MODEL_MOONSHOTAI_KIMI_K2_5, MODEL_OPENAI_GPT_5_1_CHAT, MODEL_OPENAI_GPT_5_1_CODEX, MODEL_OPENAI_GPT_5_MINI, MODEL_OPENAI_GPT_5_NANO, MODEL_OPENAI_GPT_4O, MODEL_OPENAI_GPT_4_1_MINI, MODEL_OPENAI_GPT_4_1_NANO, MODEL_ANTHROPIC_CLAUDE_OPUS_4, MODEL_ANTHROPIC_CLAUDE_SONNET_4, MODEL_ANTHROPIC_CLAUDE_3_7_SONNET, MODEL_ANTHROPIC_CLAUDE_3_5_SONNET, MODEL_ANTHROPIC_CLAUDE_4_5_HAIKU, MODEL_GOOGLE_GEMINI_2_5_PRO, MODEL_GOOGLE_GEMINI_2_5_FLASH, MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE_PREVIEW_09_2025, MODEL_ZAI_GLM_4_7_FLASH, MODEL_ZAI_GLM_4_5_AIR, MODEL_ZAI_GLM_4_5_AIR_FREE, OPENROUTER_FREE_MODELS, isOpenRouterVisionModel, } from '../../../constants/openrouter';
1
+ import { MODEL_GPT_OSS_20B_FREE, MODEL_MOONSHOTAI_KIMI_K2_5, MODEL_OPENAI_GPT_5_1_CHAT, MODEL_OPENAI_GPT_5_1_CODEX, MODEL_OPENAI_GPT_5_MINI, MODEL_OPENAI_GPT_5_NANO, MODEL_OPENAI_GPT_4O, MODEL_OPENAI_GPT_4_1_MINI, MODEL_OPENAI_GPT_4_1_NANO, MODEL_ANTHROPIC_CLAUDE_OPUS_4, MODEL_ANTHROPIC_CLAUDE_SONNET_4, MODEL_ANTHROPIC_CLAUDE_3_7_SONNET, MODEL_ANTHROPIC_CLAUDE_3_5_SONNET, MODEL_ANTHROPIC_CLAUDE_4_5_HAIKU, MODEL_GOOGLE_GEMINI_2_5_PRO, MODEL_GOOGLE_GEMINI_2_5_FLASH, MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE_PREVIEW_09_2025, MODEL_ZAI_GLM_4_7_FLASH, MODEL_ZAI_GLM_4_5_AIR, MODEL_ZAI_GLM_4_5_AIR_FREE, OPENROUTER_FREE_MODELS, isOpenRouterFreeModel, isOpenRouterVisionModel, } from '../../../constants/openrouter';
2
2
  import { OpenRouterChatService } from './OpenRouterChatService';
3
3
  import { resolveVisionModel } from '../../../utils';
4
4
  /**
@@ -106,7 +106,7 @@ export class OpenRouterChatServiceProvider {
106
106
  * @returns True if the model is free
107
107
  */
108
108
  isModelFree(model) {
109
- return OPENROUTER_FREE_MODELS.includes(model) || model.endsWith(':free');
109
+ return isOpenRouterFreeModel(model);
110
110
  }
111
111
  }
112
112
  //# sourceMappingURL=OpenRouterChatServiceProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"OpenRouterChatServiceProvider.js","sourceRoot":"","sources":["../../../../../src/services/providers/openrouter/OpenRouterChatServiceProvider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OpenRouterChatServiceProvider.js","sourceRoot":"","sources":["../../../../../src/services/providers/openrouter/OpenRouterChatServiceProvider.ts"],"names":[],"mappings":"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"}
@@ -10,4 +10,5 @@ export * from './openaiCompatibleSse';
10
10
  export * from './openaiCompatibleTools';
11
11
  export * from './processChatFlow';
12
12
  export * from './visionModelResolver';
13
+ export * from './refreshOpenRouterFreeModels';
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC"}
@@ -10,4 +10,5 @@ export * from './openaiCompatibleSse';
10
10
  export * from './openaiCompatibleTools';
11
11
  export * from './processChatFlow';
12
12
  export * from './visionModelResolver';
13
+ export * from './refreshOpenRouterFreeModels';
13
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface RefreshOpenRouterFreeModelsOptions {
2
+ apiKey: string;
3
+ endpoint?: string;
4
+ modelsEndpoint?: string;
5
+ timeoutMs?: number;
6
+ concurrency?: number;
7
+ maxCandidates?: number;
8
+ maxWorking?: number;
9
+ appName?: string;
10
+ appUrl?: string;
11
+ }
12
+ export interface RefreshOpenRouterFreeModelsFailure {
13
+ id: string;
14
+ reason: string;
15
+ }
16
+ export interface RefreshOpenRouterFreeModelsResult {
17
+ working: string[];
18
+ failed: RefreshOpenRouterFreeModelsFailure[];
19
+ fetchedAt: number;
20
+ }
21
+ /**
22
+ * Fetch currently available OpenRouter free models and probe each model.
23
+ */
24
+ export declare function refreshOpenRouterFreeModels(options: RefreshOpenRouterFreeModelsOptions): Promise<RefreshOpenRouterFreeModelsResult>;
25
+ //# sourceMappingURL=refreshOpenRouterFreeModels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshOpenRouterFreeModels.d.ts","sourceRoot":"","sources":["../../../src/utils/refreshOpenRouterFreeModels.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,kCAAkC;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kCAAkC;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iCAAiC;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,kCAAkC,EAAE,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AA6MD;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,kCAAkC,GAC1C,OAAO,CAAC,iCAAiC,CAAC,CAoE5C"}
@@ -0,0 +1,204 @@
1
+ import { ENDPOINT_OPENROUTER_API, isOpenRouterFreeModel, } from '../constants/openrouter';
2
+ const OPENROUTER_MODELS_ENDPOINT = 'https://openrouter.ai/api/v1/models';
3
+ const DEFAULT_CONCURRENCY = 2;
4
+ const DEFAULT_TIMEOUT_MS = 12000;
5
+ const DEFAULT_MAX_CANDIDATES = 1;
6
+ const DEFAULT_MAX_WORKING = 10;
7
+ function toErrorMessage(error) {
8
+ if (error instanceof Error) {
9
+ return error.message;
10
+ }
11
+ return String(error);
12
+ }
13
+ function normalizeLimit(value, fallback) {
14
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
15
+ return fallback;
16
+ }
17
+ return Math.max(1, Math.floor(value));
18
+ }
19
+ function normalizeModelIds(modelIds) {
20
+ const seen = new Set();
21
+ const normalized = [];
22
+ for (const modelId of modelIds) {
23
+ const trimmed = modelId.trim();
24
+ if (!trimmed || seen.has(trimmed)) {
25
+ continue;
26
+ }
27
+ seen.add(trimmed);
28
+ normalized.push(trimmed);
29
+ }
30
+ return normalized;
31
+ }
32
+ async function fetchWithTimeout(url, init, timeoutMs) {
33
+ const hasAbortController = typeof AbortController !== 'undefined';
34
+ if (!hasAbortController) {
35
+ return fetch(url, init);
36
+ }
37
+ const controller = new AbortController();
38
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
39
+ try {
40
+ return await fetch(url, {
41
+ ...init,
42
+ signal: controller.signal,
43
+ });
44
+ }
45
+ catch (error) {
46
+ if (error instanceof Error && error.name === 'AbortError') {
47
+ throw new Error(`Timeout after ${timeoutMs}ms`);
48
+ }
49
+ throw error;
50
+ }
51
+ finally {
52
+ clearTimeout(timeoutId);
53
+ }
54
+ }
55
+ async function toHttpFailureReason(response) {
56
+ const status = `HTTP ${response.status} ${response.statusText}`.trim();
57
+ let body = '';
58
+ try {
59
+ body = await response.text();
60
+ }
61
+ catch {
62
+ return status;
63
+ }
64
+ const compactBody = body.replace(/\s+/g, ' ').trim().slice(0, 200);
65
+ if (!compactBody) {
66
+ return status;
67
+ }
68
+ return `${status}: ${compactBody}`;
69
+ }
70
+ function extractModelIds(payload) {
71
+ if (!payload || typeof payload !== 'object') {
72
+ throw new Error('Invalid models response shape');
73
+ }
74
+ const response = payload;
75
+ const modelEntries = Array.isArray(response.data)
76
+ ? response.data
77
+ : Array.isArray(response.models)
78
+ ? response.models
79
+ : [];
80
+ if (modelEntries.length === 0) {
81
+ return [];
82
+ }
83
+ const modelIds = modelEntries
84
+ .map((entry) => entry.id)
85
+ .filter((id) => typeof id === 'string');
86
+ return normalizeModelIds(modelIds);
87
+ }
88
+ async function probeModel({ modelId, apiKey, endpoint, timeoutMs, appName, appUrl, }) {
89
+ const headers = {
90
+ Authorization: `Bearer ${apiKey}`,
91
+ 'Content-Type': 'application/json',
92
+ };
93
+ if (appName) {
94
+ headers['X-Title'] = appName;
95
+ }
96
+ if (appUrl) {
97
+ headers['HTTP-Referer'] = appUrl;
98
+ }
99
+ try {
100
+ const response = await fetchWithTimeout(endpoint, {
101
+ method: 'POST',
102
+ headers,
103
+ body: JSON.stringify({
104
+ model: modelId,
105
+ messages: [{ role: 'user', content: 'Reply only with OK.' }],
106
+ stream: false,
107
+ }),
108
+ }, timeoutMs);
109
+ if (!response.ok) {
110
+ return {
111
+ id: modelId,
112
+ ok: false,
113
+ reason: await toHttpFailureReason(response),
114
+ };
115
+ }
116
+ try {
117
+ await response.json();
118
+ }
119
+ catch (error) {
120
+ return {
121
+ id: modelId,
122
+ ok: false,
123
+ reason: `JSON parse failed: ${toErrorMessage(error)}`,
124
+ };
125
+ }
126
+ return { id: modelId, ok: true };
127
+ }
128
+ catch (error) {
129
+ return {
130
+ id: modelId,
131
+ ok: false,
132
+ reason: toErrorMessage(error),
133
+ };
134
+ }
135
+ }
136
+ async function runWithConcurrency(items, concurrency, worker) {
137
+ if (items.length === 0) {
138
+ return [];
139
+ }
140
+ const results = new Array(items.length);
141
+ let nextIndex = 0;
142
+ const workerCount = Math.min(concurrency, items.length);
143
+ await Promise.all(Array.from({ length: workerCount }, async () => {
144
+ while (nextIndex < items.length) {
145
+ const current = nextIndex;
146
+ nextIndex += 1;
147
+ results[current] = await worker(items[current]);
148
+ }
149
+ }));
150
+ return results;
151
+ }
152
+ /**
153
+ * Fetch currently available OpenRouter free models and probe each model.
154
+ */
155
+ export async function refreshOpenRouterFreeModels(options) {
156
+ const apiKey = options.apiKey.trim();
157
+ if (!apiKey) {
158
+ throw new Error('OpenRouter API key is required.');
159
+ }
160
+ const modelsEndpoint = options.modelsEndpoint || OPENROUTER_MODELS_ENDPOINT;
161
+ const endpoint = options.endpoint || ENDPOINT_OPENROUTER_API;
162
+ const timeoutMs = normalizeLimit(options.timeoutMs, DEFAULT_TIMEOUT_MS);
163
+ const concurrency = normalizeLimit(options.concurrency, DEFAULT_CONCURRENCY);
164
+ const maxCandidates = normalizeLimit(options.maxCandidates, DEFAULT_MAX_CANDIDATES);
165
+ const maxWorking = normalizeLimit(options.maxWorking, DEFAULT_MAX_WORKING);
166
+ const modelsResponse = await fetchWithTimeout(modelsEndpoint, { method: 'GET' }, timeoutMs);
167
+ if (!modelsResponse.ok) {
168
+ throw new Error(await toHttpFailureReason(modelsResponse));
169
+ }
170
+ let payload;
171
+ try {
172
+ payload = await modelsResponse.json();
173
+ }
174
+ catch (error) {
175
+ throw new Error(`JSON parse failed: ${toErrorMessage(error)}`);
176
+ }
177
+ const candidateModelIds = extractModelIds(payload)
178
+ .filter((modelId) => isOpenRouterFreeModel(modelId))
179
+ .slice(0, maxCandidates);
180
+ const probeResults = await runWithConcurrency(candidateModelIds, concurrency, (modelId) => probeModel({
181
+ modelId,
182
+ apiKey,
183
+ endpoint,
184
+ timeoutMs,
185
+ appName: options.appName,
186
+ appUrl: options.appUrl,
187
+ }));
188
+ const working = probeResults
189
+ .filter((result) => result.ok)
190
+ .map((result) => result.id)
191
+ .slice(0, maxWorking);
192
+ const failed = probeResults
193
+ .filter((result) => !result.ok)
194
+ .map((result) => ({
195
+ id: result.id,
196
+ reason: result.reason || 'Unknown error',
197
+ }));
198
+ return {
199
+ working,
200
+ failed,
201
+ fetchedAt: Date.now(),
202
+ };
203
+ }
204
+ //# sourceMappingURL=refreshOpenRouterFreeModels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refreshOpenRouterFreeModels.js","sourceRoot":"","sources":["../../../src/utils/refreshOpenRouterFreeModels.ts"],"names":[],"mappings":"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"}
@@ -72,6 +72,7 @@ var AITuberOnAirChat = (() => {
72
72
  MODEL_GEMINI_2_5_FLASH_LITE: () => MODEL_GEMINI_2_5_FLASH_LITE,
73
73
  MODEL_GEMINI_2_5_FLASH_LITE_PREVIEW_06_17: () => MODEL_GEMINI_2_5_FLASH_LITE_PREVIEW_06_17,
74
74
  MODEL_GEMINI_2_5_PRO: () => MODEL_GEMINI_2_5_PRO,
75
+ MODEL_GEMINI_3_1_FLASH_LITE_PREVIEW: () => MODEL_GEMINI_3_1_FLASH_LITE_PREVIEW,
75
76
  MODEL_GEMINI_3_1_PRO_PREVIEW: () => MODEL_GEMINI_3_1_PRO_PREVIEW,
76
77
  MODEL_GEMINI_3_FLASH_PREVIEW: () => MODEL_GEMINI_3_FLASH_PREVIEW,
77
78
  MODEL_GEMINI_3_PRO_PREVIEW: () => MODEL_GEMINI_3_PRO_PREVIEW,
@@ -142,6 +143,7 @@ var AITuberOnAirChat = (() => {
142
143
  parseOpenAICompatibleTextStream: () => parseOpenAICompatibleTextStream,
143
144
  parseOpenAICompatibleToolStream: () => parseOpenAICompatibleToolStream,
144
145
  processChatWithOptionalTools: () => processChatWithOptionalTools,
146
+ refreshOpenRouterFreeModels: () => refreshOpenRouterFreeModels,
145
147
  resolveVisionModel: () => resolveVisionModel,
146
148
  runOnceText: () => runOnceText,
147
149
  screenplayToText: () => screenplayToText,
@@ -196,6 +198,7 @@ var AITuberOnAirChat = (() => {
196
198
  // src/constants/gemini.ts
197
199
  var ENDPOINT_GEMINI_API = "https://generativelanguage.googleapis.com";
198
200
  var MODEL_GEMINI_3_1_PRO_PREVIEW = "gemini-3.1-pro-preview";
201
+ var MODEL_GEMINI_3_1_FLASH_LITE_PREVIEW = "gemini-3.1-flash-lite-preview";
199
202
  var MODEL_GEMINI_3_PRO_PREVIEW = "gemini-3-pro-preview";
200
203
  var MODEL_GEMINI_3_FLASH_PREVIEW = "gemini-3-flash-preview";
201
204
  var MODEL_GEMINI_2_5_PRO = "gemini-2.5-pro";
@@ -206,6 +209,7 @@ var AITuberOnAirChat = (() => {
206
209
  var MODEL_GEMINI_2_0_FLASH_LITE = "gemini-2.0-flash-lite";
207
210
  var GEMINI_VISION_SUPPORTED_MODELS = [
208
211
  MODEL_GEMINI_3_1_PRO_PREVIEW,
212
+ MODEL_GEMINI_3_1_FLASH_LITE_PREVIEW,
209
213
  MODEL_GEMINI_3_PRO_PREVIEW,
210
214
  MODEL_GEMINI_3_FLASH_PREVIEW,
211
215
  MODEL_GEMINI_2_5_PRO,
@@ -291,7 +295,7 @@ var AITuberOnAirChat = (() => {
291
295
  var OPENROUTER_FREE_DAILY_LIMIT_HIGH_CREDITS = 1e3;
292
296
  var OPENROUTER_CREDITS_THRESHOLD = 10;
293
297
  function isOpenRouterFreeModel(model) {
294
- return OPENROUTER_FREE_MODELS.some((freeModel) => model.includes(freeModel));
298
+ return model.trim().endsWith(":free");
295
299
  }
296
300
  function isOpenRouterVisionModel(model) {
297
301
  return OPENROUTER_VISION_SUPPORTED_MODELS.some(
@@ -792,6 +796,212 @@ If it's in another language, summarize in that language.
792
796
  return resolved;
793
797
  };
794
798
 
799
+ // src/utils/refreshOpenRouterFreeModels.ts
800
+ var OPENROUTER_MODELS_ENDPOINT = "https://openrouter.ai/api/v1/models";
801
+ var DEFAULT_CONCURRENCY = 2;
802
+ var DEFAULT_TIMEOUT_MS = 12e3;
803
+ var DEFAULT_MAX_CANDIDATES = 1;
804
+ var DEFAULT_MAX_WORKING = 10;
805
+ function toErrorMessage(error) {
806
+ if (error instanceof Error) {
807
+ return error.message;
808
+ }
809
+ return String(error);
810
+ }
811
+ function normalizeLimit(value, fallback) {
812
+ if (typeof value !== "number" || !Number.isFinite(value)) {
813
+ return fallback;
814
+ }
815
+ return Math.max(1, Math.floor(value));
816
+ }
817
+ function normalizeModelIds(modelIds) {
818
+ const seen = /* @__PURE__ */ new Set();
819
+ const normalized = [];
820
+ for (const modelId of modelIds) {
821
+ const trimmed = modelId.trim();
822
+ if (!trimmed || seen.has(trimmed)) {
823
+ continue;
824
+ }
825
+ seen.add(trimmed);
826
+ normalized.push(trimmed);
827
+ }
828
+ return normalized;
829
+ }
830
+ async function fetchWithTimeout(url, init, timeoutMs) {
831
+ const hasAbortController = typeof AbortController !== "undefined";
832
+ if (!hasAbortController) {
833
+ return fetch(url, init);
834
+ }
835
+ const controller = new AbortController();
836
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
837
+ try {
838
+ return await fetch(url, {
839
+ ...init,
840
+ signal: controller.signal
841
+ });
842
+ } catch (error) {
843
+ if (error instanceof Error && error.name === "AbortError") {
844
+ throw new Error(`Timeout after ${timeoutMs}ms`);
845
+ }
846
+ throw error;
847
+ } finally {
848
+ clearTimeout(timeoutId);
849
+ }
850
+ }
851
+ async function toHttpFailureReason(response) {
852
+ const status = `HTTP ${response.status} ${response.statusText}`.trim();
853
+ let body = "";
854
+ try {
855
+ body = await response.text();
856
+ } catch {
857
+ return status;
858
+ }
859
+ const compactBody = body.replace(/\s+/g, " ").trim().slice(0, 200);
860
+ if (!compactBody) {
861
+ return status;
862
+ }
863
+ return `${status}: ${compactBody}`;
864
+ }
865
+ function extractModelIds(payload) {
866
+ if (!payload || typeof payload !== "object") {
867
+ throw new Error("Invalid models response shape");
868
+ }
869
+ const response = payload;
870
+ const modelEntries = Array.isArray(response.data) ? response.data : Array.isArray(response.models) ? response.models : [];
871
+ if (modelEntries.length === 0) {
872
+ return [];
873
+ }
874
+ const modelIds = modelEntries.map((entry) => entry.id).filter((id) => typeof id === "string");
875
+ return normalizeModelIds(modelIds);
876
+ }
877
+ async function probeModel({
878
+ modelId,
879
+ apiKey,
880
+ endpoint,
881
+ timeoutMs,
882
+ appName,
883
+ appUrl
884
+ }) {
885
+ const headers = {
886
+ Authorization: `Bearer ${apiKey}`,
887
+ "Content-Type": "application/json"
888
+ };
889
+ if (appName) {
890
+ headers["X-Title"] = appName;
891
+ }
892
+ if (appUrl) {
893
+ headers["HTTP-Referer"] = appUrl;
894
+ }
895
+ try {
896
+ const response = await fetchWithTimeout(
897
+ endpoint,
898
+ {
899
+ method: "POST",
900
+ headers,
901
+ body: JSON.stringify({
902
+ model: modelId,
903
+ messages: [{ role: "user", content: "Reply only with OK." }],
904
+ stream: false
905
+ })
906
+ },
907
+ timeoutMs
908
+ );
909
+ if (!response.ok) {
910
+ return {
911
+ id: modelId,
912
+ ok: false,
913
+ reason: await toHttpFailureReason(response)
914
+ };
915
+ }
916
+ try {
917
+ await response.json();
918
+ } catch (error) {
919
+ return {
920
+ id: modelId,
921
+ ok: false,
922
+ reason: `JSON parse failed: ${toErrorMessage(error)}`
923
+ };
924
+ }
925
+ return { id: modelId, ok: true };
926
+ } catch (error) {
927
+ return {
928
+ id: modelId,
929
+ ok: false,
930
+ reason: toErrorMessage(error)
931
+ };
932
+ }
933
+ }
934
+ async function runWithConcurrency(items, concurrency, worker) {
935
+ if (items.length === 0) {
936
+ return [];
937
+ }
938
+ const results = new Array(items.length);
939
+ let nextIndex = 0;
940
+ const workerCount = Math.min(concurrency, items.length);
941
+ await Promise.all(
942
+ Array.from({ length: workerCount }, async () => {
943
+ while (nextIndex < items.length) {
944
+ const current = nextIndex;
945
+ nextIndex += 1;
946
+ results[current] = await worker(items[current]);
947
+ }
948
+ })
949
+ );
950
+ return results;
951
+ }
952
+ async function refreshOpenRouterFreeModels(options) {
953
+ const apiKey = options.apiKey.trim();
954
+ if (!apiKey) {
955
+ throw new Error("OpenRouter API key is required.");
956
+ }
957
+ const modelsEndpoint = options.modelsEndpoint || OPENROUTER_MODELS_ENDPOINT;
958
+ const endpoint = options.endpoint || ENDPOINT_OPENROUTER_API;
959
+ const timeoutMs = normalizeLimit(options.timeoutMs, DEFAULT_TIMEOUT_MS);
960
+ const concurrency = normalizeLimit(options.concurrency, DEFAULT_CONCURRENCY);
961
+ const maxCandidates = normalizeLimit(
962
+ options.maxCandidates,
963
+ DEFAULT_MAX_CANDIDATES
964
+ );
965
+ const maxWorking = normalizeLimit(options.maxWorking, DEFAULT_MAX_WORKING);
966
+ const modelsResponse = await fetchWithTimeout(
967
+ modelsEndpoint,
968
+ { method: "GET" },
969
+ timeoutMs
970
+ );
971
+ if (!modelsResponse.ok) {
972
+ throw new Error(await toHttpFailureReason(modelsResponse));
973
+ }
974
+ let payload;
975
+ try {
976
+ payload = await modelsResponse.json();
977
+ } catch (error) {
978
+ throw new Error(`JSON parse failed: ${toErrorMessage(error)}`);
979
+ }
980
+ const candidateModelIds = extractModelIds(payload).filter((modelId) => isOpenRouterFreeModel(modelId)).slice(0, maxCandidates);
981
+ const probeResults = await runWithConcurrency(
982
+ candidateModelIds,
983
+ concurrency,
984
+ (modelId) => probeModel({
985
+ modelId,
986
+ apiKey,
987
+ endpoint,
988
+ timeoutMs,
989
+ appName: options.appName,
990
+ appUrl: options.appUrl
991
+ })
992
+ );
993
+ const working = probeResults.filter((result) => result.ok).map((result) => result.id).slice(0, maxWorking);
994
+ const failed = probeResults.filter((result) => !result.ok).map((result) => ({
995
+ id: result.id,
996
+ reason: result.reason || "Unknown error"
997
+ }));
998
+ return {
999
+ working,
1000
+ failed,
1001
+ fetchedAt: Date.now()
1002
+ };
1003
+ }
1004
+
795
1005
  // src/services/providers/claude/ClaudeChatService.ts
796
1006
  var ClaudeChatService = class {
797
1007
  /**
@@ -2038,6 +2248,7 @@ If it's in another language, summarize in that language.
2038
2248
  getSupportedModels() {
2039
2249
  return [
2040
2250
  MODEL_GEMINI_3_1_PRO_PREVIEW,
2251
+ MODEL_GEMINI_3_1_FLASH_LITE_PREVIEW,
2041
2252
  MODEL_GEMINI_3_PRO_PREVIEW,
2042
2253
  MODEL_GEMINI_3_FLASH_PREVIEW,
2043
2254
  MODEL_GEMINI_2_5_PRO,
@@ -3358,7 +3569,7 @@ If it's in another language, summarize in that language.
3358
3569
  * @returns True if the model is free
3359
3570
  */
3360
3571
  isModelFree(model) {
3361
- return OPENROUTER_FREE_MODELS.includes(model) || model.endsWith(":free");
3572
+ return isOpenRouterFreeModel(model);
3362
3573
  }
3363
3574
  };
3364
3575