Dos formas de integrar un LLM gratuito en un script de Python
Cuando la gente busca llms gratuitos para python, generalmente se refiere a una de dos cosas. La primera es un modelo de pesos abiertos que descargas y ejecutas tú mismo, usando algo como Ollama, llama.cpp o la biblioteca transformers de Hugging Face. La segunda es un endpoint alojado que cuesta tan poco, o viene con suficiente crédito de prueba, que parece gratuito para un prototipo. Ambas son legítimas y se adaptan a diferentes tareas.
Ejecutar localmente significa no pagar por token y que tus datos no salgan de tu máquina. También significa que necesitas suficiente RAM o memoria de GPU para el modelo que elijas, esperas a que se descarguen los archivos y los modelos pequeños que caben en un portátil responden peor que los grandes. Una API alojada elimina la necesidad de hardware, pero pagas por token una vez que se agota la prueba. Esta página cubre ambos honestamente y luego muestra el código de Python para la opción alojada en este sitio, que es compatible con OpenAI y funciona con el paquete estándar openai.
Modelos locales y API alojada lado a lado
| Pregunta | Ejecútalo tú mismo | API alojada prepago |
|---|---|---|
| Costo por token | Ninguno, pero pagas por hardware y electricidad | $0,15 por 1M de entrada, $0,50 por 1M de salida |
| Tiempo de configuración | Instala el entorno de ejecución, descarga los pesos y ajusta la configuración | Instala un paquete, pega una clave |
| Longitud del contexto | Limitado por tu memoria | 100.000 tokens, prompt más completación |
| Concurrencia | Lo que tu máquina pueda manejar | 8 peticiones a la vez con una clave completa, 2 con una clave de prueba |
| Rechazos | Depende de los pesos que elijas | Ajustado para responder sin rechazos de contenido para uso adulto legal |
| Uso sin conexión | Sí | No |
Si tienes una GPU capaz y tu trabajo es privado o sin conexión, lo local es una buena solución y puedes dejar de leer aquí. Si quieres un modelo más grande sin poseer la máquina, o quieres que tu script se comporte igual en un portátil, un servidor y un runner de CI, la ruta alojada es más sencilla. Muchos proyectos usan ambos: un modelo local pequeño durante el desarrollo y el alojado en producción, cambiando solo la URL base.
Configura el endpoint alojado en cinco minutos
Primero necesitas una clave. Regístrate con Google o con un correo electrónico y contraseña, luego reclama los $0.50 de crédito de prueba con un clic en la página de claves de API. La prueba es válida por 7 días, una por persona y no requiere pago. Luego instala el SDK y mantén la clave fuera de tu código fuente.
pip install openai
export NOTRACK_API_KEY="sk-your-key-here"
El cliente necesita dos cambios respecto a una configuración estándar de OpenAI: la URL base y la clave. El ID del modelo es uncensored y solo se ofrece un modelo.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwvideogenerator.cc/v1",
api_key=os.environ["NOTRACK_API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a concise writing assistant."},
{"role": "user", "content": "Give me three title ideas for a noir short story."},
],
temperature=0.8,
)
print(resp.choices[0].message.content)
print(resp.usage)
La autenticación es un encabezado estándar Authorization: Bearer, que el SDK añade por ti. Existen dos métodos: POST /v1/chat/completions y GET /v1/models. Puedes confirmar la conexión con client.models.list(), que devuelve el ID del modelo disponible. La lista completa de parámetros está en la página de documentación.
Respuestas en streaming
Para cualquier salida visible, activa stream=True e itera sobre los chunks al llegar los eventos enviados por el servidor. El uso se reporta al final del stream, así que puedes registrar los conteos de tokens.
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Write a four-line poem about rain on a tin roof."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
usage = getattr(chunk, "usage", None)
if usage:
print("\n", usage)
Un detalle que suele sorprender: una petición en streaming mantiene su slot de concurrencia hasta que el stream termina. Con una clave de prueba de 2 slots, dos streams abiertos significan que una tercera llamada obtendrá un 429 con el código concurrency. Siempre consume o cierra el stream.
Llamadas a herramientas y modo JSON
El endpoint soporta tools, tool_choice, tool_calls en streaming y resultados devueltos como role: "tool". Aquí tienes un ciclo de ida y vuelta completo con una función.
import json
tools = [{
"type": "function",
"function": {
"name": "word_count",
"description": "Count the words in a piece of text.",
"parameters": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
},
},
}]
messages = [{"role": "user", "content": "How many words are in: the quick brown fox jumps"}]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = len(args["text"].split())
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": str(result),
})
final = client.chat.completions.create(model="uncensored", messages=messages, tools=tools)
print(final.choices[0].message.content)
Analiza siempre arguments de forma defensiva; el modelo lo devuelve como una cadena JSON y cualquier modelo puede producir ocasionalmente uno mal formado. Para salida estructurada sin herramientas, usa el modo JSON.
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "Reply with a JSON object with keys title and tags (list of strings)."},
{"role": "user", "content": "Describe a lighthouse keeper's diary."},
],
response_format={"type": "json_object"},
temperature=0.2,
)
data = json.loads(resp.choices[0].message.content)
print(data["title"], data["tags"])
Menciona la palabra JSON y nombra las claves en tu prompt, y mantén la temperatura baja. Envuelve json.loads en un bloque try para código de producción.
Límites de peticiones, errores y reintentos
Una clave completa permite 8 solicitudes concurrentes y 300 solicitudes por minuto. Una clave de prueba permite 2 concurrentes y 60 por minuto. Si excedes, obtienes HTTP 429 con código rate_limited (demasiadas por minuto) o concurrency (demasiadas a la vez). En sobrecarga temporal, la API devuelve 429 con cabecera Retry-After: 5. El SDK reintenta algunos errores; para scripts con fan-out, un wrapper explícito es más claro.
import time
from concurrent.futures import ThreadPoolExecutor
from openai import RateLimitError
def ask(prompt, tries=4):
for attempt in range(tries):
try:
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
max_tokens=300,
)
return r.choices[0].message.content
except RateLimitError:
time.sleep(5 * (attempt + 1))
raise RuntimeError("still rate limited")
prompts = [f"Summarize idea number {i} in one sentence." for i in range(20)]
with ThreadPoolExecutor(max_workers=2) as pool: # 8 on a full key
results = list(pool.map(ask, prompts))
Ajustar max_workers al límite de concurrencia de tu clave evita la mayoría de los 429s antes de que ocurran. Cada respuesta también incluye X-Request-Id, X-Balance-USD y dos encabezados de límite de tasa, que puedes leer a través de client.chat.completions.with_raw_response.create(...) cuando los necesites para depuración o soporte.
Cómo elegir entre local y alojado
Omite la ideología y responde cuatro preguntas sobre tu proyecto.
- ¿Los datos deben permanecer en tu máquina? Si un contrato o un hábito lo exige, ejecútalo localmente. Los prompts alojados no se usan para entrenamiento aquí, pero sí viajan por la red.
- ¿Cuánta memoria tienes? El tamaño del modelo determina la calidad. Un portátil sin GPU dedicada te limita a modelos pequeños y las respuestas lo reflejarán. Un modelo alojado te da uno grande con una ventana de contexto de 100,000 tokens, difícil de igualar en casa.
- ¿Cuántas peticiones enviarás? Unas pocas cientos por semana es trivial en ambos lados. Cientos de miles al mes convierten la factura alojada en un número real, y una máquina local empieza a parecer atractiva si está inactiva de otra manera. Calcula la suma: 100,000 peticiones de 1,000 tokens de entrada y 500 de salida cuestan 100,000 x $0.0004 = $40.
- ¿Necesitas que las negativas se manejen de una manera específica? Muchos modelos de pesos abiertos vienen con comportamiento de negativa incorporado, y otros están ajustados para eliminarlo. El modelo alojado aquí está ajustado para responder sin negativas de contenido para uso adulto legal, así que no necesitas buscar los pesos correctos.
Si tus respuestas apuntan en ambas direcciones, empieza con la opción alojada durante la primera semana. La prueba no cuesta nada y aprenderás qué necesitan tus prompts antes de comprar hardware o descargar otro archivo de varios gigabytes.
Errores comunes en scripts de Python
La mayoría de los problemas que encuentran las personas son pequeños y solucionables.
- URL base incorrecta. Debe terminar en
/v1. La falta de sufijo devuelve un 404, no un mensaje útil. Imprimeclient.base_urlsi tienes dudas. - Clave en el código. Léela de una variable de entorno y añade tu archivo
.enva.gitignore. - Nombre de modelo incorrecto. El único ID de modelo es
uncensored. Los nombres de otros proveedores no funcionan; cámbialos al portar código. - Bucles sin límite. Un bucle de llamadas a herramientas que nunca termina es un error costoso. Límita a, por ejemplo, cinco rondas.
- Ignorar el presupuesto de contexto. El prompt y la completación comparten los 100,000 tokens. Si añades cada turno para siempre, eventualmente alcanzarás el límite. Recorta o resume mensajes antiguos.
- Asume que JSON es siempre válido. Incluso en modo JSON, verifica las claves que necesitas antes de usarlas.
Cuando algo se ve mal en el lado del servidor, cita el valor de X-Request-Id de la respuesta cuando escribas a support. Esto permite a los operadores encontrar tu llamada exacta.
Lo que cuesta la opción alojada para Python
Un script típico con 1.000 tokens de entrada y 500 de salida cuesta $0,00015 más $0,00025, es decir, $0,0004. El crédito de prueba de $0,50 cubre aproximadamente 1.250. Una recarga de $20, el mínimo, cubre 50.000. El crédito no caduca, las peticiones fallidas o rechazadas no se cobran y los prompts no se usan para entrenamiento. Las recargas van hasta $500 con un bono a partir de $50, y el pago es en cripto, USDT en TRC20 o USDC en Base. Consulta la página de precios para ver los detalles.
Es solo texto: sin imágenes, audio, video, embeddings ni fine-tuning. Y la regla estricta es que no hay contenido sexual con menores. Para un proyecto en Python que necesite generación de texto con menos rechazos, esa es toda la superficie.
