Juanoto2012 commited on
Commit
a457776
·
verified ·
1 Parent(s): 55f152f

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +39 -16
server.js CHANGED
@@ -19,12 +19,18 @@ const PROVIDERS = [
19
  url: "https://api.llm7.io/v1/chat/completions"
20
  },
21
  {
22
- id: "aquadevs",
23
- url: "https://api.aquadevs.com/v1/chat/completions",
24
- imageUrl: "https://api.aquadevs.com/v1/images/generations",
25
- apiKey: "aqua_sk_4f51287cffce411e8c19afacc833481d"
26
  },
27
  {
 
 
 
 
 
 
28
  id: "ventarys-mirror",
29
  url: "https://ventarys-mirror-1.hf.space/v1/chat/completions",
30
  proxySecret: "sk-52650650a50f0v10vg150vs0v"
@@ -47,12 +53,13 @@ function isModelAllowed(modelId, modelObj = null) {
47
  return !BLOCKED_TIERS.some(keyword => lowerId.includes(keyword));
48
  }
49
 
50
- let currentLoad = { "llm7": 0, "aquadevs": 0, "ventarys-mirror": 0 };
51
 
52
- // --- LÍMITE DE TASA (10 por minuto) ---
 
53
  const limiter = rateLimit({
54
  windowMs: 60 * 1000,
55
- max: 10,
56
  message: { error: { message: "Límite alcanzado. Espera 1 minuto entre mensajes.", code: 429 } },
57
  standardHeaders: true,
58
  legacyHeaders: false,
@@ -68,10 +75,12 @@ app.get('/health', (req, res) => {
68
  });
69
  });
70
 
 
71
  app.get('/v1/models', async (req, res) => {
72
  try {
73
  const fetchPromises = PROVIDERS.map(async (provider) => {
74
- const modelsUrl = provider.url.replace("/chat/completions", "/models");
 
75
  const fetchHeaders = { "Content-Type": "application/json" };
76
 
77
  if (provider.apiKey) fetchHeaders["Authorization"] = `Bearer ${provider.apiKey}`;
@@ -81,10 +90,24 @@ app.get('/v1/models', async (req, res) => {
81
  if (!resp.ok) throw new Error(`HTTP Error ${resp.status}`);
82
 
83
  const json = await resp.json();
84
- if (json && Array.isArray(json.data)) {
85
- return json.data
86
- .filter(model => isModelAllowed(model.id, model))
87
- .map(model => ({ ...model, owned_by: provider.id }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
  return [];
90
  });
@@ -101,12 +124,12 @@ app.get('/v1/models', async (req, res) => {
101
  }
102
  });
103
 
104
- // --- RUTA PRINCIPAL DE GENERACIÓN (Con Límite de Tasa) ---
105
  app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req, res) => {
106
  const isImage = req.path === '/v1/images/generations';
107
  const requestedModel = req.body.model;
108
 
109
- // Filtro de Seguridad
110
  if (requestedModel && !isModelAllowed(requestedModel)) {
111
  return res.status(403).json({
112
  error: {
@@ -120,7 +143,7 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
120
  const startTime = Date.now();
121
  let selectedProvider = null;
122
 
123
- // Sala de Espera
124
  while (Date.now() - startTime < QUEUE_TIMEOUT) {
125
  let shuffled = [...availableProviders].sort(() => Math.random() - 0.5);
126
  for (let provider of shuffled) {
@@ -179,7 +202,7 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
179
 
180
  stream.on('end', releaseSlot);
181
  stream.on('error', releaseSlot);
182
- req.on('close', releaseSlot); // Libera si el usuario cierra la pestaña antes de terminar
183
  } else {
184
  releaseSlot();
185
  res.end();
 
19
  url: "https://api.llm7.io/v1/chat/completions"
20
  },
21
  {
22
+ // Nuevo proveedor: Pollinations AI
23
+ id: "pollinations",
24
+ url: "https://text.pollinations.ai/openai",
25
+ modelsUrl: "https://text.pollinations.ai/models" // Ruta específica para extraer modelos
26
  },
27
  {
28
+ // Nuevo proveedor gratuito y sin key
29
+ id: "airforce",
30
+ url: "https://api.airforce/v1/chat/completions"
31
+ },
32
+ {
33
+ // Tu espejo seguro
34
  id: "ventarys-mirror",
35
  url: "https://ventarys-mirror-1.hf.space/v1/chat/completions",
36
  proxySecret: "sk-52650650a50f0v10vg150vs0v"
 
53
  return !BLOCKED_TIERS.some(keyword => lowerId.includes(keyword));
54
  }
55
 
56
+ let currentLoad = { "llm7": 0, "pollinations": 0, "airforce": 0, "ventarys-mirror": 0 };
57
 
58
+ // --- LÍMITE DE TASA (Para producción en ventarys.net) ---
59
+ // Te lo subí a 25 peticiones por minuto para que tus usuarios no se topen con el error 429
60
  const limiter = rateLimit({
61
  windowMs: 60 * 1000,
62
+ max: 25,
63
  message: { error: { message: "Límite alcanzado. Espera 1 minuto entre mensajes.", code: 429 } },
64
  standardHeaders: true,
65
  legacyHeaders: false,
 
75
  });
76
  });
77
 
78
+ // --- EXTRACCIÓN Y FILTRO DE MODELOS MULTIPROVEEDOR ---
79
  app.get('/v1/models', async (req, res) => {
80
  try {
81
  const fetchPromises = PROVIDERS.map(async (provider) => {
82
+ // Usa la URL específica de modelos si existe, si no, deduce la estándar
83
+ const modelsUrl = provider.modelsUrl || provider.url.replace("/chat/completions", "/models");
84
  const fetchHeaders = { "Content-Type": "application/json" };
85
 
86
  if (provider.apiKey) fetchHeaders["Authorization"] = `Bearer ${provider.apiKey}`;
 
90
  if (!resp.ok) throw new Error(`HTTP Error ${resp.status}`);
91
 
92
  const json = await resp.json();
93
+ let modelsArray = [];
94
+
95
+ // Traductor automático: Algunas APIs devuelven un array directo (Pollinations),
96
+ // otras devuelven un objeto con la propiedad "data" (OpenAI Standard)
97
+ if (Array.isArray(json)) {
98
+ modelsArray = json;
99
+ } else if (json && Array.isArray(json.data)) {
100
+ modelsArray = json.data;
101
+ }
102
+
103
+ if (modelsArray.length > 0) {
104
+ return modelsArray
105
+ .filter(model => isModelAllowed(model.id || model.name, model))
106
+ .map(model => ({
107
+ ...model,
108
+ id: model.id || model.name, // Asegura que tu frontend siempre vea un "id"
109
+ owned_by: provider.id
110
+ }));
111
  }
112
  return [];
113
  });
 
124
  }
125
  });
126
 
127
+ // --- RUTA PRINCIPAL DE GENERACIÓN ---
128
  app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req, res) => {
129
  const isImage = req.path === '/v1/images/generations';
130
  const requestedModel = req.body.model;
131
 
132
+ // Filtro de Seguridad de Tiers
133
  if (requestedModel && !isModelAllowed(requestedModel)) {
134
  return res.status(403).json({
135
  error: {
 
143
  const startTime = Date.now();
144
  let selectedProvider = null;
145
 
146
+ // Sala de Espera / Balanceo
147
  while (Date.now() - startTime < QUEUE_TIMEOUT) {
148
  let shuffled = [...availableProviders].sort(() => Math.random() - 0.5);
149
  for (let provider of shuffled) {
 
202
 
203
  stream.on('end', releaseSlot);
204
  stream.on('error', releaseSlot);
205
+ req.on('close', releaseSlot);
206
  } else {
207
  releaseSlot();
208
  res.end();