Entradas Mensuales

Síguenos en:

Canal Oficial Telegram de elhacker.NET Grupo Facebook elhacker.NET Twitter elhacker.NET Canal Youtube elhacker.NET Comunidad Steam: Grupo elhacker.NET Mastodon

Entradas populares

PostHeaderIcon Ejemplos y funcionamiento avanzado de la API de Ollama


 Como vimos en la entrada aanterior, Ollama nos permite interactuar con LLMs de una forma sencilla y transparente. Mucho más allá de realizar ciertas pruebas sencillas o iniciarnos en el mundo de los LLMs para comprobar su funcionamiento, en el mundo de desarrollo software se suelen necesitar opciones más avanzadas y personalizadas. En este entrada exploraremos estas opciones avanzadas que nos ofrece Ollama para poder personalizar nuestros modelos, así como hacer uso de su API y poder sacarle todo el partido para crear aplicaciones de IA.

 

 

Además de los comandos interactivos, Ollama proporciona un API para poder interactuar con los LLMs. Este API es útil sobre todo en casos en los que se quiera realizar una integración con otras aplicaciones (como por ejemplo desde Spring AI), puesto que lo único que habría que hacer es realizar una llamada al endpoint Rest correspondiente.

Por defecto, el API está expuesta en el puerto 11434 (http://localhost:11434 o http://127.0.0.1:11434), aunque es configurable. Los endpoints que expone el API son:

  • /api/generate: método POST. Endpoint para obtener respuestas del LLM con un único prompt que no sea con una secuencia de pregunta-respuesta. Óptimo para tareas como predicción de textos o traducción.
  • /api/chat: método POST. Endpoint para interacción pregunta-respuesta conversacional.
  • /api/embeddings: método POST. Genera embeddings (representaciones vectoriales numéricas) del input.
  • /api/tags: método GET. Equivalente al comando interactivo ollama list con el que se muestra un listado de modelos existentes en el sistema.
  • /api/show: método POST. Equivalente al comando interactivo ollama show con el que se obtiene información sobre un modelo concreto como parámetros, plantillas, licencias, etc.
  • /api/delete: método DELETE. Equivalente al comando interactivo ollama rm para eliminar un modelo del sistema local.
  • /api/pull: método POST. Equivalente al comando interactivo ollama pull para descargar un modelo desde el repositorio remoto al sistema local.
  • /api/create: método POST. Equivalente al comando interactivo ollama create con el que se crea un nuevo modelo personalizado a partir de un Modelfile.
  • /api/copy: método POST. Equivalente al comando interactivo ollama cp para copiar un modelo en el sistema local.
  • /api/push: método POST. Endpoint para subir un modelo al repositorio remoto.
Verás que en algunos ejemplos en vez de usar la API en local http://localhost:11434 se usa https://api.elhacker.net, puedes usar la gratuitamente para:

  • /api/generate
  • /api/chat
Y debes usar el modelo: gemma3:27b-it-fp16. Si necesitas integrarla en una aplicación web, contacta con webmaster@elhacker.net para añadir OLLAMA_ORIGINS con tu URL.

Compatibilidad con API OpenAI

Como es bien conocido, OpenAI es uno de los primeros y más importantes impulsores de esta nueva ola de IA-LLMs, por lo que es lógico pensar que muchas aplicaciones hacen uso de sus APIs. Para mantener las integraciones con el resto de aplicaciones de la forma más sencilla posible, Ollama añade una capa para que las APIs sean compatibles con el formato de OpenAI.

En base, lo que sucede es que, además de los endpoints comentados anteriormente, se exponen otros sobre el path /v1/ (http://localhost:11434/v1/) con el formato del API de OpenAI. Algunos de los endpoints compatibles actualmente disponibles son:

  • /v1/chat/completions: sería el equivalente al endpoint /api/chat.
  • /v1/embeddings: sería el equivalente al endpoint /api/embeddings.
  • /v1/models: sería el equivalente al endpoint /api/tags.

Con esto en mente, muchas de las librerías que se integran con el API de OpenAI solo necesitarían adaptar algunos parámetros de configuración para usar Ollama. Algunos de estos parámetros son:

  • base_url: simplemente indicar el endpoint v1 de Ollama: http://localhost:11434/v1/
  • api_key: simplemente informar cualquier valor no vacío, si bien es cierto que dependiendo de la librería, podría ser necesario indicar algún valor más específico como “nokey” o el correspondiente. En Ollama en realidad no se valida este parámetro.

Parámetros
  • MESSAGE: instrucción con ejemplos de turnos de conversación para que el LLM pueda seguir un estilo o formato en concreto. Los roles posibles son:
    • System: una forma alternativa de indicar el prompt del sistema.
    • User: ejemplos de lo que puede preguntar el usuario.
    • Assistant: ejemplos de cómo debe responder el modelo.

Curl y Jq


Podemos utilizar cURL para llamar a las API con Jq. Para instalar JQ, siga sus guías oficiales de descarga. Por ejemplo, en un Mac, puede ejecutar brew install jq; de forma similar, en una máquina Ubuntu, puede ejecutar sudo apt-get install jq; en una máquina Windows, puede utilizar chocolatey. O bien, puede obtener el binario y hacerlo ejecutable.Puede utilizar otros lenguajes de programación, como Python y JavaScript, con las bibliotecas oficiales para Python y JavaScript, y marcos como LiteLLM o LangChain para llamar a las API de Ollama.

jq es una herramienta ligera y flexible de línea de comandos para procesar JSON en Linux y otros sistemas operativos. Te permite leer, filtrar, mapear y transformar datos estructurados con la misma facilidad que sed, awk, grep y amigos te permiten jugar con texto. Por lo general debes instalarla usando: «sudo dnf install jq» o «sudo aptinstall jq» dependiendo de tu distribución de Linux.

 Peticiones básicas simples

Curl básico

curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2",
  "prompt": "Why is the sky blue?"
}'

Curl para Windows


curl --location --request POST "https://api.elhacker.net/api/chat" ^
--header "Content-Type: application/json" ^
--data-raw "{    \"model\": \"gemma3:27b-it-fp16\",    \"messages\": [        {            \"role\": \"user\",            \"content\": \"why is the sky blue?\"        }    ]}" 

En PowerShell para Windows


(Invoke-WebRequest -method POST -Body '{"model":"gemma3:27b-it-fp16", "prompt":"hola", "stream": false}' -uri https://api.elhacker.net/api/generate ).Content | ConvertFrom-json 

Otro ejemplo PowerShell para Windows


$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")

$body = "{`n    `"model`": `"gemma3:27b-it-fp16`",`n    `"messages`": [`n        {`n            `"role`": `"user`",`n            `"content`": `"why is the sky blue?`"`n        }`n    ]`n}"

$response = Invoke-RestMethod 'https://api.elhacker.net/api/chat' -Method 'POST' -Headers $headers -Body $body
$response | ConvertTo-Json

En Python (ejemplo chat.py)

import requests, json

# Define the local Ollama API endpoint
url = "https://api.elhacker.net/api/generate"

# Send a prompt to the Gemma 3 model
payload = {
    "model": "gemma3:27b-it-fp16",
    "prompt": "Write a short story about space exploration."
}

# stream=True tells requests to read the response as a live data stream
response = requests.post(url, json=payload, stream=True)

# Ollama sends one JSON object per line as it generates text
for line in response.iter_lines():
    if line:
        data = json.loads(line.decode("utf-8"))
        # Each chunk has a "response" key containing part of the text
        if "response" in data:
            print(data["response"], end="", flush=True) 


Librería Ollama


pip install ollama


from ollama import chat

stream = chat(
    model='gemma3:27b-it-fp16',
    messages=[{'role': 'user', 'content': 'Why is the sky blue?'}],
    stream=True,
)

for chunk in stream:
  print(chunk['message']['content'], end='', flush=True) 

Javascript

    async function callStreamingAPI() {
      try {
        const response = await fetch('https://api.elhacker.net/api/generate', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: 'gemma3:27b-it-fp16',
            prompt: 'hola'
          })
        });

Explicación


  • async function callStreamingAPI(): Define una función asíncrona para realizar la llamada a la API.
  • fetch(): Envía una solicitud HTTP POST a la URL especificada con el Content-Type y el cuerpo indicados.
  • JSON.stringify(): Convierte el objeto JavaScript en una cadena JSON para enviarla en el cuerpo de la solicitud. 

Streming la respuesta

        const reader = response.body.getReader();
        const decoder = new TextDecoder('utf-8');
        const outputElement = document.getElementById('output');

        let done = false;
        while (!done) {
          const { value, done: readerDone } = await reader.read();
          done = readerDone;

          if (value) {
            const chunk = decoder.decode(value, { stream: true });
            const parts = chunk.split('\n').filter(Boolean);

            parts.forEach(part => {
              try {
                const json = JSON.parse(part);
                outputElement.innerHTML += json.response;
              } catch (err) {
                console.error('Error parsing chunk:', err);
              }
            });
          }
        }
      } catch (error) {
        console.error('Error calling the API:', error);
      }
    }


Explicación

  • response.body.getReader(): Accede a un ReadableStream desde la respuesta, lo que le permite leer los datos en fragmentos.
  • new TextDecoder(“utf-8”): Decodifica los fragmentos de la respuesta desde un formato binario a texto legible.
  • let done = false: Un indicador para controlar el bucle de lectura del flujo.
  • while (!done): Repite el bucle hasta llegar al final del flujo.
  • await reader.read(): Lee el siguiente fragmento del flujo. El resultado contiene value (el fragmento de datos) y done (un valor booleano que indica si el flujo ha finalizado).
  • decoder.decode(value, { stream: true }): Decodifica el fragmento de datos binarios en una cadena UTF-8.
  •  chunk.split(“\n”).filter(Boolean): Divide la cadena decodificada en partes JSON separadas basadas en saltos de línea y filtra las cadenas vacías.
  •  JSON.parse(part): Analiza cada parte como JSON.
  •  outputElement.innerHTML += json.response: Añade el campo de respuesta de cada objeto JSON al div de salida.
  • catch (error): Detecta y registra cualquier error que se produzca durante la obtención o el procesamiento del flujo.


Test ejemplo navegador

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Streaming API Example</title>
</head>
<body>
  <h1>Streaming API Response</h1>
  <div id="output"></div>

  <script>
    async function callStreamingAPI() {
      input_user = prompt("Enter Text here : ")
      try {
        const response = await fetch('http://localhost:11434/api/generate', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: 'llama3.2',
            prompt: input_user
          })
        });

        // Read the response as a stream of text
        const reader = response.body.getReader();
        const decoder = new TextDecoder('utf-8');
        const outputElement = document.getElementById('output');

        let done = false;
        while (!done) {
          const { value, done: readerDone } = await reader.read();
          done = readerDone;

          if (value) {
            // Decode and split by newline to parse each JSON object
            const chunk = decoder.decode(value, { stream: true });
            const parts = chunk.split('\n').filter(Boolean);

            parts.forEach(part => {
              try {
                const json = JSON.parse(part);
                outputElement.innerHTML += json.response; // Append response text
              } catch (err) {
                console.error('Error parsing chunk:', err);
              }
            });
          }
        }
      } catch (error) {
        console.error('Error calling the API:', error);
      }
    }

    // Call the function when the script loads
    callStreamingAPI();
  </script>
</body>
</html>


En PHP

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
   CURLOPT_URL => 'http://localhost:11434/api/chat',
   CURLOPT_RETURNTRANSFER => true,
   CURLOPT_ENCODING => '',
   CURLOPT_MAXREDIRS => 10,
   CURLOPT_TIMEOUT => 0,
   CURLOPT_FOLLOWLOCATION => true,
   CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
   CURLOPT_CUSTOMREQUEST => 'POST',
   CURLOPT_POSTFIELDS =>'{
    "model": "llama3.2",
    "messages": [
        {
            "role": "user",
            "content": "Hello, please introduce yourself"
        }
    ],
    "stream": false
}',
   CURLOPT_HTTPHEADER => array(
      'Content-Type: application/json'
   ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;


Resolución de problemas relacionados con CORS (opcional)

Acerca de la política CORS si se encuentra con este problema

Cuando se produce el error «El acceso a fetch en “http://localhost:11434/api/chat” desde el origen “null” ha sido bloqueado por la política CORS: la respuesta a la solicitud previa no supera la comprobación de control de acceso: No hay ningún encabezado «Access-Control-Allow-Origin» en el recurso solicitado», significa que el servidor no está configurado para permitir solicitudes de diferentes orígenes. Este problema se debe a la política de intercambio de recursos entre orígenes (CORS), que restringe las solicitudes de las páginas web a un dominio diferente al que sirve la página web.


Para solucionar este problema, puedes:


  • 1.  Configurar el servidor para permitir CORS: asegúrese de que el servidor al que intenta acceder tiene el encabezado Access-Control-Allow-Origin configurado para permitir solicitudes desde el origen de su aplicación web (o utilice * para permitir todos los orígenes con fines de prueba).
  •     Utilizar extensiones del navegador: si no tiene acceso para modificar el servidor o necesita una solución rápida con fines de desarrollo, utilice extensiones del navegador como Moesif Origin & CORS Changer o CORS Unblock. Estas extensiones pueden anular la política CORS predeterminada del navegador para permitir la solicitud.
  •     Establezca el modo de solicitud en «no-cors»: aunque esto puede permitir que ciertas solicitudes eludan las comprobaciones CORS, tiene limitaciones, ya que solo recibirá respuestas opacas que no incluyen contenido. Esto no es ideal para la mayoría de los casos de uso que requieren la recuperación de datos.
Ejemplo: 

  • export OLLAMA_ORIGINS = http://localhost:3000,http://192.168.1.50:8080 (Permite solicitudes desde estos dos orígenes específicos).

  • OLLAMA_ORIGINS=https://ia.elhacker.info,https://ns2.elhacker.net,https://ia.elhacker.net Ejemplo: export OLLAMA_ORIGINS=“*” (permite solicitudes desde cualquier origen. Úselo con precaución, especialmente si OLLAMA_HOST no es 127.0.0.1, ya que esto podría exponer su API ampliamente). Puede incluir protocolos como chrome-extension://*.

Query api chat


curl https://api.elhacker.net/api/chat -d '{
  "model": "gemma3:27b-it-fp16",
  "stream": false,
  "messages": [
    {
      "role": "user",
      "content": "hola"
    }
  ]
}'
Response api chat

  • message content


Query api generate


curl https://api.elhacker.net/api/generate -d '{
  "model": "gemma3:27b-it-fp16",
  "stream": false,
  "prompt": "hola"
}'


Response api generate

  • response 

 



Explorando la API de búsqueda web de Ollama 

La API de búsqueda web de Ollama ofrece resultados estructurados. Los usuarios especifican consultas y un `max_results` opcional. La respuesta incluye fragmentos, URLs y metadatos. Este formato ayuda en el análisis para los agentes.

Para integrar, los desarrolladores usan librerías de Python. Instala con pip install ollama. Luego, llama a ollama.web_search(query="example"). La función maneja la autenticación a través de variables de entorno.

De manera similar, los usuarios de JavaScript aprovechan ollama-js. Importa el módulo e invoca Ollama().webSearch({query: "example"}). Los ejemplos en los repositorios demuestran el manejo de errores y los reintentos.

cURL proporciona una opción de bajo nivel. Crea solicitudes con encabezados para Autorización. Este enfoque es adecuado para scripting o pruebas.

Sin embargo, Apidog mejora este proceso. Como herramienta de gestión de API, Apidog visualiza los puntos finales y los parámetros. Genera fragmentos de código para la API de Ollama, acelerando el desarrollo.

La API soporta contextos largos. Los resultados pueden abarcar miles de tokens. Por lo tanto, los modelos con ventanas extendidas funcionan mejor. Ollama recomienda un mínimo de 32,000 tokens.

Además, el punto final de obtención complementa la búsqueda. Extrae contenido de URLs, evitando la necesidad de un navegador directo. Combina ambos para agentes completos.

La seguridad sigue siendo primordial. Ollama requiere claves API, lo que evita el acceso no autorizado. Los usuarios generan claves en https://ollama.com/settings/keys.

Pasando al uso práctico, considera un agente de investigación. El agente consulta la búsqueda web, obtiene páginas y sintetiza respuestas. Este flujo de trabajo supera a los modelos estáticos.

Esta integración desbloquea la búsqueda web en los clientes. Los modelos llaman a las herramientas dinámicamente, mejorando la interactividad.

Además, el servidor MCP soporta extensiones. Los desarrolladores añaden herramientas personalizadas para correo electrónico, GitHub o imágenes. Esta flexibilidad posiciona a Ollama como infraestructura para agentes.

En Windows con NVIDIA, las instalaciones incluyen controladores CUDA. Linux usa Docker para el aislamiento. macOS se beneficia de la aceleración nativa.

Pasando a configuraciones avanzadas, agrupa múltiples servidores MCP. Esto distribuye las cargas para escalas empresariales.

La API de búsqueda web cobra por llamada en planes avanzados. Sin embargo, los niveles gratuitos cubren la mayoría de las necesidades.

Compara con alternativas: la búsqueda de OpenAI cuesta $10 por cada 1k llamadas. Ollama supera esto para usuarios que priorizan lo local.


0 comentarios :

Publicar un comentario

Los comentarios pueden ser revisados en cualquier momento por los moderadores.

Serán publicados aquellos que cumplan las siguientes condiciones:
- Comentario acorde al contenido del post.
- Prohibido mensajes de tipo SPAM.
- Evite incluir links innecesarios en su comentario.
- Contenidos ofensivos, amenazas e insultos no serán permitidos.

Debe saber que los comentarios de los lectores no reflejan necesariamente la opinión del STAFF.