Saltar al contenido principal

Documentación de la API LLMaaS

URL base

https://api.ai.cloud-temple.com/v1

Autenticación

Todas las solicitudes requieren un encabezado Authorization con su token de API:

Authorization: Bearer SU_TOKEN_API

Límite de tasas y facturación

El Principio de los Tiers: Nivel de Acceso, Presupuesto y Capacidad

Nuestro sistema de tiers está diseñado como envolturas completas de servicio que definen tres aspectos clave de su uso:

  1. Un Nivel de Acceso (Crédito de Compra): Para los Tiers 1 a 4, se trata de una cantidad que debe pagarse de forma anticipada (upfront) para activar el servicio y desbloquear las capacidades técnicas y presupuestarias del nivel elegido.
  2. Un Límite de Presupuesto Mensual: Es el tope de su consumo mensual, asegurándole un control total sobre sus costos.
  3. Una Capacidad Técnica: Son los límites de rendimiento (tokens por día y por hora) que garantizan un rendimiento estable y predecible para su volumen de llamadas.

La elección de un tier, por tanto, representa un equilibrio entre la inversión inicial, el presupuesto mensual previsto y la capacidad técnica requerida. Su consumo dentro de esta envoltura se facturará posteriormente según las tarifas vigentes.

Table of Tiers

TierPurchase CreditMonthly LimitTokens Output/HourTokens Output/DayDescription
Tier 1200 €1,000 €150,0003,600,000Standard usage
Tier 2500 €3,000 €300,0007,200,000Professional usage
Tier 31,000 €5,000 €450,00010,800,000High volume
Tier 44,000 €10,000 €600,00014,400,000Enterprise
Monthly BillingN/AUnlimitedHigh priorityHigh priorityContact sales

Note: Rate limits are calculated based on output tokens. Token pricing varies by usage:

  • Input tokens: 1.90 € / million
  • Output tokens (standard): 8.00 € / million
  • Output tokens (reasoner): 8.00 € / million (applies to most advanced models for complex agent-like or reasoning tasks)

Facturación de audio

  • Transcripción de audio: 0,01 € / minuto (cada minuto comenzado está sujeto a cargo)

Límites de encabezados

Las respuestas incluyen encabezados informativos:

X-RateLimit-Limit-Requests: 1000
X-RateLimit-Remaining-Requests: 999
X-RateLimit-Reset-Requests: 1640995200

Error 429 - Límite alcanzado

{
"error": {
"message": "Se ha excedido el límite de tasa. Por favor, actualice su nivel o inténtelo de nuevo más tarde.",
"type": "rate_limit_error",
"code": "rate_limit_exceeded"
}
}

Endpoints

POST /v1/chat/completions

Genera respuestas conversacionales.

Petición

curl -X POST "https://api.ai.cloud-temple.com/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SU_TOKEN_API" \
-d '{
"model": "granite3.3:8b",
"messages": [
{
"role": "user",
"content": "Explique la fotosíntesis"
}
],
"max_tokens": 200,
"temperature": 0.7
}'

Parámetros

ParámetroTipoObligatorioDescripción
modelstringID del modelo (ver catálogo)
messagesarrayConversación (role: system/user/assistant)
streambooleanActiva el streaming (por defecto: false)
temperaturefloatCreatividad 0.0-2.0 (por defecto: 0.7)
max_tokensintegerLímite de tokens (por defecto: 1024)
top_pfloatMuestreo nucleus 0.0-1.0 (por defecto: 1.0)
presence_penaltyfloatPenalización de presencia -2.0 a 2.0 (por defecto: 0)
frequency_penaltyfloatPenalización de frecuencia -2.0 a 2.0 (por defecto: 0)
userstringID de usuario único
toolsarrayLista de herramientas que el modelo puede invocar.
tool_choicestring/objectControla si el modelo debe invocar una herramienta. "none", "auto", o {"type": "function", "function": {"name": "my_function"}}.

Respuesta estándar

{
"id": "chatcmpl-bc52de347f2e4068b7bde380c0f8db37",
"object": "chat.completion",
"created": 1749114814,
"model": "granite3.3:8b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "La fotosíntesis es un proceso biológico..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 15,
"completion_tokens": 42,
"total_tokens": 57
}
}

Respuesta con Llamada a Herramienta

Si el modelo decide llamar a una herramienta, la respuesta tendrá un finish_reason de tool_calls y el mensaje contendrá un array tool_calls.

{
"id": "chatcmpl-9f27a53f52b44a9693753f2a5e1f7a73",
"object": "chat.completion",
"created": 1749115200,
"model": "qwen3:14b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_current_weather",
"arguments": "{\n \"location\": \"París, Francia\",\n \"unit\": \"celsius\"\n}"
}
}
]
},
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 82,
"completion_tokens": 18,
"total_tokens": 100
}
}

Después de recibir una respuesta tool_calls, usted debe ejecutar la herramienta desde su lado y luego enviar el resultado al modelo utilizando un mensaje con el role: "tool".

{
"model": "qwen3:14b",
"messages": [
{
"role": "user",
"content": "¿Qué tiempo hace en París?"
},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"París, Francia\", \"unit\": \"celsius\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\": \"22\", \"unit\": \"celsius\", \"description\": \"Despejado\"}"
}
]
}

Streaming (SSE)

Con "stream": true, la respuesta llega token por token:

Encabezados de respuesta:

Content-Type: text/event-stream
Cache-Control: no-cache

Formato de los eventos:

data: {"choices":[{"delta":{"content":"La"},"finish_reason":null,"index":0}],"created":1749114814,"id":"chatcmpl-bc52de347f2e4068b7bde380c0f8db37","model":"granite3.3:8b","object":"chat.completion.chunk"}

data: {"choices":[{"delta":{"content":" foto"},"finish_reason":null,"index":0}],"created":1749114814,"id":"chatcmpl-bc52de347f2e4068b7bde380c0f8db37","model":"granite3.3:8b","object":"chat.completion.chunk"}

data: {"choices":[{"delta":{"content":""},"finish_reason":"stop","index":0}],"created":1749114814,"id":"chatcmpl-bc52de347f2e4068b7bde380c0f8db37","model":"granite3.3:8b","object":"chat.completion.chunk"}

data: [DONE]

Estructura de los chunks:

  • choices[].delta.content: Contenido incremental
  • finish_reason: null durante el streaming, luego "stop"
  • Señal de finalización: data: [DONE]

Consultas Multimodales (Visión)

Para analizar imágenes, puedes enviar una solicitud donde el campo content de un mensaje del usuario sea un arreglo (array) que contenga tanto texto como imágenes.

El formato para una imagen es un objeto con type: "image_url" y un campo image_url que contiene la URL de la imagen en formato data URI (base64).

Nota de Compatibilidad

Aunque el formato estándar y recomendado es {"type": "image_url", "image_url": {"url": "data:..."}}, la API también admite, por motivos de flexibilidad, un formato simplificado {"type": "image", "image": "data:..."}. Sin embargo, se recomienda utilizar el formato estándar image_url para una mejor compatibilidad con el ecosistema OpenAI.

OCR y Análisis de Documentos

Para tareas específicas de análisis de documentos (PDF, escaneos, tablas), recomendamos el uso del modelo especializado DeepSeek-OCR. Consulta la documentación dedicada.

Ejemplo de solicitud de Visión

curl -X POST "https://api.ai.cloud-temple.com/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SU_TOKEN_API" \
-d '{
"model": "gemma3:27b",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "¿Qué ves en esta imagen?"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,..."
}
}
]
}
],
"max_tokens": 500
}'

POST /v1/completions

aviso

Nota : El endpoint /v1/completions utiliza el mismo formato que /v1/chat/completions con mensajes.
Para completar texto simple, utiliza un mensaje de tipo user con tu prompt.

Completado de texto mediante formato de chat.

Petición

curl -X POST "https://api.ai.cloud-temple.com/v1/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SU_TOKEN_API" \
-d '{
"model": "granite3.3:8b",
"messages": [
{
"role": "user",
"content": "Complete esta frase: La inteligencia artificial es"
}
],
"max_tokens": 100,
"temperature": 0.7
}'

Parámetros

Idénticos a /v1/chat/completions – ver sección anterior.

Respuesta

Formato idéntico a /v1/chat/completions.

POST /v1/audio/transcriptions

Transcripción de audio a texto (Whisper).

Petición

curl -X POST "https://api.ai.cloud-temple.com/v1/audio/transcriptions" \
-H "Authorization: Bearer SU_TOKEN_API" \
-F "file=@audio.wav" \
-F "language=fr" \
-F "response_format=json"

Parámetros

ParámetroTipoObligatorioDescripción
filebinaryArchivo de audio (wav, mp3, m4a).
languagestringCódigo de idioma ISO 639-1 (por ejemplo: "fr"). Detección automática si no se proporciona.
initial_promptstringContexto o palabras específicas para mejorar la precisión de la transcripción.
taskstringTarea a realizar: transcribe (por defecto) o translate (traducir al inglés).
response_formatstringjson (por defecto, equivalente a verbose_json). Los formatos text, srt, vtt no están actualmente soportados.

Respuesta (json)

{
"text": "Hola, esto es una prueba de transcripción de audio.",
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.0,
"text": " Hola, esto es una prueba de transcripción de audio.",
"tokens": [ 50364, 40365, 33, 2373, 359, 456, 2373, 323, 1330, 2373, 2264, 50564 ],
"temperature": 0.0,
"avg_logprob": -0.25,
"compression_ratio": 1.5,
"no_speech_prob": 0.05
}
],
"language": "es"
}

POST /v1/embeddings

Creates an embedding vector representing the input text.

Petición

curl -X POST "https://api.ai.cloud-temple.com/v1/embeddings" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SU_TOKEN_API" \
-d '{
"model": "granite-embedding:278m",
"input": "El texto a vectorizar"
}'

Parámetros

ParámetroTipoObligatorioDescripción
modelstringID del modelo de embedding (ver catálogo)
inputstring o array de stringsEl texto o la lista de textos a vectorizar.

Respuesta

{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [
0.018902843818068504,
-0.023282647132873535,
...
-0.016484618186950684
]
}
],
"model": "granite-embedding:278m",
"usage": {
"prompt_tokens": 5,
"total_tokens": 5
}
}

GET /v1/models

List of available models.

Petición

curl -X GET "https://api.ai.cloud-temple.com/v1/models" \
-H "Authorization: Bearer SU_TOKEN_API"

Respuesta

{
"object": "list",
"data": [
{
"id": "granite3.3:8b",
"object": "model",
"created": 1749110897,
"owned_by": "CloudTemple",
"root": "granite3.3:8b",
"aliases": ["granite3.3:8b"],
"max_model_len": 60000,
"permission": [
{
"id": "modelperm-granite3.3:8b-1749110897",
"object": "model_permission",
"allow_sampling": true,
"allow_view": true,
"allow_fine_tuning": false
}
]
}
]
}

Códigos de error

400 - Petición Inválida

{
"error": {
"message": "Parámetro 'temperature' no válido: debe estar entre 0 y 2",
"type": "invalid_request_error",
"param": "temperature"
}
}

401 - No autorizado

{
"error": {
"message": "Clave de API inválida proporcionada",
"type": "authentication_error"
}
}

404 - Modelo No Encontrado

{
"error": {
"message": "El modelo 'unknown-model' no existe",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}

429 - Límite de tasa

{
"error": {
"message": "Se ha excedido el límite de tasa. Por favor, actualice su nivel o inténtelo de nuevo más tarde.",
"type": "rate_limit_error",
"code": "rate_limit_exceeded"
}
}

500 - Error del servidor

{
"error": {
"message": "Error interno del servidor",
"type": "error_del_servidor"
}
}

503 - Servicio No Disponible

{
"error": {
"message": "Servicio temporalmente no disponible",
"type": "service_unavailable_error"
}
}

Ejemplos por lenguaje

Python con requests

import requests
import json

# Configuración
# It is recommended to protect your API key by using environment variables.
# Ejemplo: API_KEY = os.getenv("LLMAAS_API_KEY")
API_KEY = "SU_TOKEN_API"
BASE_URL = "https://api.ai.cloud-temple.com/v1"

headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}

# Completado de chat
payload = {
"model": "granite3.3:8b",
"messages": [
{"role": "user", "content": "¡Hola!"}
],
"max_tokens": 100
}

try:
response = requests.post(
f"{BASE_URL}/chat/completions",
headers=headers,
json=payload,
timeout=30 # Adición de un timeout para la solicitud
)

response.raise_for_status() # Lanza una excepción para códigos de error HTTP (4xx, 5xx)
result = response.json()
print(result["choices"][0]["message"]["content"])

except requests.exceptions.HTTPError as e:
print(f"Error HTTP: {e.response.status_code} - {e.response.text}")
except requests.exceptions.RequestException as e:
print(f"Error de red: {e}")
except json.JSONDecodeError:
print(f"Error de decodificación JSON: {response.text}")
except Exception as e:
print(f"Se ha producido un error inesperado: {e}")

Python con Streaming

import requests
import json

def stream_chat(message, model="granite3.3:8b"):
# Se recomienda proteger su clave API utilizando variables de entorno.
# Ejemplo: API_KEY = os.getenv("LLMAAS_API_KEY")
API_KEY = "SU_TOKEN_API"
BASE_URL = "https://api.ai.cloud-temple.com/v1"

headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}

payload = {
"model": model,
"messages": [{"role": "user", "content": message}],
"stream": True,
"max_tokens": 200
}

try:
response = requests.post(
f"{BASE_URL}/chat/completions",
headers=headers,
json=payload,
stream=True,
timeout=30 # Adición de un timeout para la solicitud
)

response.raise_for_status() # Lanza una excepción para códigos de error HTTP (4xx, 5xx)

for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
data = line[6:] # Eliminar 'data: '
if data == '[DONE]':
break
try:
chunk = json.loads(data)
content = chunk['choices'][0]['delta'].get('content', '')
if content:
print(content, end='', flush=True)
except json.JSONDecodeError:
print(f"Error al decodificar JSON en el stream: {data}")
continue
print() # Nueva línea después del stream
except requests.exceptions.HTTPError as e:
print(f"Error HTTP: {e.response.status_code} - {e.response.text}")
except requests.exceptions.RequestException as e:
print(f"Error de red: {e}")
except Exception as e:
print(f"Se ha producido un error inesperado: {e}")

Uso

stream_chat("Explique la física cuántica")

JavaScript/Node.js

const axios = require('axios');

// Configuración
// Se recomienda proteger su clave API utilizando variables de entorno.
// Ejemplo: const API_KEY = process.env.LLMAAS_API_KEY;
const API_KEY = 'SU_TOKEN_API';
const BASE_URL = 'https://api.ai.cloud-temple.com/v1';

async function chatCompletion(message) {
try {
const response = await axios.post(
`${BASE_URL}/chat/completions`,
{
model: 'granite3.3:8b',
messages: [
{ role: 'user', content: message }
],
max_tokens: 100
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
timeout: 30000 // Adición de un timeout para la solicitud (30 segundos)
}
);

return response.data.choices[0].message.content;
} catch (error) {
console.error('Error:', error.response?.data || error.message);
// La gestión más detallada de errores puede añadirse aquí si es necesario
// Por ejemplo: if (error.response?.status === 429) { console.error("Límite de tasa excedido"); }
}
}

// Uso
chatCompletion('¡Hola!').then(response => {
if (response) {
console.log(response);
}
});

JavaScript con Fetch (Navegador)

async function fetchCompletion(message) {
const response = await fetch('https://api.ai.cloud-temple.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: 'granite3.3:8b',
messages: [
{ role: 'user', content: message }
],
max_tokens: 100
})
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}

const data = await response.json();
return data.choices[0].message.content;
}

Buenas prácticas

Gestión de errores

def safe_api_call(payload):
try:
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if response.status_code == 429:
print("Límite de tasa alcanzado, esperar...")
time.sleep(60) # Esperar 1 minuto
return safe_api_call(payload) # Reintentar
else:
print(f"Error HTTP: {e}")
except requests.exceptions.RequestException as e:
print(f"Error de red: {e}")

Optimización de Costos

  1. Utilice modelos adecuados: Modelos más pequeños para pruebas
  2. Limitar max_tokens: Evite respuestas demasiado largas
  3. Reutilice conversaciones: Ventana de contexto eficiente
  4. Monitoreo: Supervise su uso en la Consola

Seguridad

  1. Proteja su token: Variables de entorno
  2. Rotación regular: Cambie sus claves periódicamente
  3. Validación de entrada: Limpie los datos del usuario
  4. Límite de tasa para el cliente: Implemente sus propios límites

SDK and Integrations

The LLMaaS API is compatible with existing OpenAI SDKs by simply changing the base URL:

SDK de Python de OpenAI

from openai import OpenAI

# It is recommended to protect your API key by using environment variables.
# Ejemplo: api_key=os.getenv("LLMAAS_API_KEY")
client = OpenAI(
api_key="SU_TOKEN_API",
base_url="https://api.ai.cloud-temple.com/v1"
)

try:
response = client.chat.completions.create(
model="granite3.3:8b",
messages=[
{"role": "user", "content": "¡Hola!"}
],
max_tokens=50 # Adición de max_tokens para mantener coherencia con las pruebas
)

print(response.choices[0].message.content)

except Exception as e:
print(f"Error del SDK OpenAI: {e}")

LangChain

from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage

# Configuración del modelo de chat (compatible con LLMaaS)
# It is recommended to protect your API key by using environment variables.
# Example: api_key=os.getenv("LLMAAS_API_KEY")
chat = ChatOpenAI(
api_key="SU_TOKEN_API",
base_url="https://api.ai.cloud-temple.com/v1",
model="granite3.3:8b",
# Note: Parameters such as max_tokens are passed via model_kwargs
# to ensure compatibility across different versions of LangChain.
model_kwargs={"max_tokens": 200}
)

try:
# Usage with messages
messages = [HumanMessage(content="Explain AI in 3 sentences")]
response = chat.invoke(messages)
print(response.content)

# Or with a simple string
response = chat.invoke("Hello, how are you?")
print(response.content)

except Exception as e:
print(f"LangChain error: {e}")

Uso de Embeddings

Incompatibilidad con clientes estándar de LangChain

Actualmente, el uso del endpoint de embeddings con las clases estándar de LangChain (langchain_openai.OpenAIEmbeddings o langchain_community.OllamaEmbeddings) presenta incompatibilidades con nuestra API.

  • OpenAIEmbeddings envía tokens precalculados en lugar de texto sin procesar, lo cual es rechazado.
  • OllamaEmbeddings no gestiona la autenticación mediante Bearer Token requerida.

Mientras se encuentra una solución permanente, se recomienda crear una clase de embeddings personalizada o llamar a la API directamente, como se muestra en el ejemplo exemplos/simple-rag-demo.

from langchain.embeddings.base import Embeddings
from typing import List
import httpx

class LLMaaSEmbeddings(Embeddings):
"""
Clase de embeddings personalizada para interactuar con la API LLMaaS de Cloud Temple.
Esta clase está diseñada para ser compatible con la interfaz `Embeddings` de LangChain,
permitiendo su uso en pipelines de LangChain mientras se llama a nuestra API específica.
"""
def __init__(self, api_key: str, base_url: str = "https://api.ai.cloud-temple.com/v1", model_name: str = "granite-embedding:278m"):
self.api_key = api_key
self.base_url = base_url
self.model_name = model_name
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}

def _embed(self, texts: List[str]) -> List[List[float]]:
payload = {"input": texts, "model": self.model_name}
try:
with httpx.Client(timeout=30.0) as client:
response = client.post(f"{self.base_url}/embeddings", headers=self.headers, json=payload)
response.raise_for_status()
data = response.json()['data']
# Ordenar los embeddings por su índice para garantizar el orden
data.sort(key=lambda e: e['index'])
return [item['embedding'] for item in data]
except httpx.HTTPStatusError as e:
print(f"Error HTTP al obtener el embedding: {e.response.status_code}")
print(f"Respuesta: {e.response.text}")
return []

def embed_documents(self, texts: List[str]) -> List[List[float]]:
return self._embed(texts)

def embed_query(self, text: str) -> List[float]:
return self._embed([text])[0]

# Uso
# embeddings = LLMaaSEmbeddings(
# api_key="SU_TOKEN_API",
# base_url="https://api.ai.cloud-temple.com/v1",
# model_name="granite-embedding:278m"
# )
# vector = embeddings.embed_query("Mi texto a vectorizar")

Soporte

  • Documentación : Guía de inicio rápido
  • Catálogo de modelos : Lista completa
  • Consola : Gestión y supervisión a través de la Consola Cloud Temple
  • Soporte : A través de la Consola Cloud Temple