API REST de Ollama
馃搶 Introducci贸n a la API REST de Ollama
Cuando instalas Ollama, autom谩ticamente obtienes un servidor REST API que se ejecuta en localhost:11434. Esta API te permite interactuar con tus modelos instalados program谩ticamente.
Verificaci贸n b谩sica
Primero, aseg煤rate que Ollama est茅 corriendo:
# En una terminal
ollama serve
# O verifica si el servicio est谩 activo
curl http://localhost:11434/馃攳 Endpoints principales de la API
1. Listar modelos disponibles
# GET /api/tags
curl http://localhost:11434/api/tagsRespuesta ejemplo:
{
"models": [
{
"name": "llama3.2:latest",
"modified_at": "2024-05-01T10:30:00Z",
"size": 1910000000,
"digest": "sha256:abc123..."
},
{
"name": "mistral:latest",
"modified_at": "2024-05-01T11:00:00Z",
"size": 4100000000
}
]
}2. Generar texto (modo simple)
# POST /api/generate
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "¿Por qu茅 el cielo es azul?",
"stream": false
}'3. Chat conversacional
# POST /api/chat
curl http://localhost:11434/api/chat -d '{
"model": "llama3.2",
"messages": [
{"role": "user", "content": "Hola, ¿c贸mo est谩s?"},
{"role": "assistant", "content": "¡Hola! Estoy bien, ¿y t煤?"},
{"role": "user", "content": "¿Puedes explicarme qu茅 es la fotos铆ntesis?"}
],
"stream": false
}'4. Crear/eliminar modelos
# POST /api/create
curl http://localhost:11434/api/create -d '{
"name": "mi-modelo-personalizado",
"modelfile": "FROM llama3.2\nSYSTEM \"Eres un chef profesional\""
}'
# DELETE /api/delete
curl -X DELETE http://localhost:11434/api/delete -d '{
"name": "mi-modelo-personalizado"
}'馃殌 S铆, puedes acceder a TODOS tus modelos instalados por la API
Respuesta clara: ¡S脥! Cualquier modelo que tengas instalado localmente con ollama pull o ollama create estar谩 disponible autom谩ticamente a trav茅s de la API REST.
Verificaci贸n pr谩ctica:
# 1. Instala un modelo si no lo tienes
ollama pull phi
# 2. Verifica que est谩 disponible en la API
curl http://localhost:11434/api/tags | grep phi
# 3. 脷salo directamente por API
curl http://localhost:11434/api/generate -d '{
"model": "phi",
"prompt": "Hola, ¿qu茅 modelo eres?",
"stream": false
}'馃捇 Consumir la API con PHP
M茅todo 1: Usando cURL nativo (recomendado)
<?php
// config.php - Configuraci贸n b谩sica
define('OLLAMA_URL', 'http://localhost:11434');
define('OLLAMA_MODEL', 'llama3.2');
// funci贸n para hacer peticiones a Ollama
function ollama_request($endpoint, $data = []) {
$url = OLLAMA_URL . $endpoint;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("Error cURL: $error");
}
curl_close($ch);
if ($http_code != 200) {
throw new Exception("HTTP Error: $http_code - $response");
}
return json_decode($response, true);
}
// Ejemplo 1: Generar texto simple
function generar_texto($prompt, $model = OLLAMA_MODEL) {
$data = [
'model' => $model,
'prompt' => $prompt,
'stream' => false,
'options' => [
'temperature' => 0.7,
'num_predict' => 500
]
];
return ollama_request('/api/generate', $data);
}
// Ejemplo 2: Chat conversacional
function chat_conversacional($mensajes, $model = OLLAMA_MODEL) {
$data = [
'model' => $model,
'messages' => $mensajes,
'stream' => false
];
return ollama_request('/api/chat', $data);
}
// Ejemplo 3: Listar modelos disponibles
function listar_modelos() {
$ch = curl_init(OLLAMA_URL . '/api/tags');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// USO PR脕CTICO
try {
// 1. Listar modelos
echo "=== Modelos instalados ===\n";
$modelos = listar_modelos();
foreach ($modelos['models'] as $modelo) {
echo "- {$modelo['name']}\n";
}
// 2. Generar respuesta
echo "\n=== Probando generaci贸n ===\n";
$resultado = generar_texto("Expl铆came qu茅 es PHP en 2 l铆neas");
echo "Respuesta: " . $resultado['response'] . "\n";
// 3. Chat conversacional
echo "\n=== Chat conversacional ===\n";
$chat = chat_conversacional([
["role" => "user", "content" => "Hola, ¿puedes ayudarme con programaci贸n?"],
["role" => "assistant", "content" => "¡Claro! Estoy aqu铆 para ayudarte. ¿En qu茅 lenguaje necesitas ayuda?"],
["role" => "user", "content" => "Quiero aprender Python, ¿por d贸nde empiezo?"]
]);
echo "Asistente: " . $chat['message']['content'] . "\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
?>M茅todo 2: Clase PHP orientada a objetos
<?php
class OllamaClient {
private $base_url;
private $default_model;
public function __construct($base_url = 'http://localhost:11434', $default_model = 'llama3.2') {
$this->base_url = rtrim($base_url, '/');
$this->default_model = $default_model;
}
private function request($method, $endpoint, $data = null) {
$url = $this->base_url . $endpoint;
$ch = curl_init($url);
$options = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_TIMEOUT => 120 // 2 minutos timeout
];
if ($method === 'POST') {
$options[CURLOPT_POST] = true;
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data);
}
} elseif ($method === 'DELETE') {
$options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data);
}
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new Exception("cURL Error: $error");
}
$decoded = json_decode($response, true);
if ($http_code >= 400) {
throw new Exception("API Error ($http_code): " . ($decoded['error'] ?? $response));
}
return $decoded;
}
// M茅todos p煤blicos
public function listModels() {
return $this->request('GET', '/api/tags');
}
public function generate($prompt, $model = null, $options = []) {
$model = $model ?: $this->default_model;
$data = array_merge([
'model' => $model,
'prompt' => $prompt,
'stream' => false
], $options);
return $this->request('POST', '/api/generate', $data);
}
public function chat($messages, $model = null, $options = []) {
$model = $model ?: $this->default_model;
$data = array_merge([
'model' => $model,
'messages' => $messages,
'stream' => false
], $options);
return $this->request('POST', '/api/chat', $data);
}
public function pullModel($model_name) {
return $this->request('POST', '/api/pull', ['name' => $model_name]);
}
public function deleteModel($model_name) {
return $this->request('DELETE', '/api/delete', ['name' => $model_name]);
}
// M茅todo para streaming (respuestas en tiempo real)
public function generateStream($prompt, $callback, $model = null) {
$model = $model ?: $this->default_model;
$url = $this->base_url . '/api/generate';
$data = [
'model' => $model,
'prompt' => $prompt,
'stream' => true
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_WRITEFUNCTION => function($ch, $data) use ($callback) {
$lines = explode("\n", $data);
foreach ($lines as $line) {
$line = trim($line);
if (!empty($line) && $line !== 'data: [DONE]') {
if (strpos($line, 'data: ') === 0) {
$json = substr($line, 6);
$decoded = json_decode($json, true);
if ($decoded && isset($decoded['response'])) {
call_user_func($callback, $decoded['response'], $decoded['done'] ?? false);
}
}
}
}
return strlen($data);
}
]);
curl_exec($ch);
curl_close($ch);
}
}
// USO DE LA CLASE
$ollama = new OllamaClient();
// 1. Ver todos los modelos
echo "=== Modelos disponibles ===\n";
$models = $ollama->listModels();
foreach ($models['models'] as $model) {
echo "馃摝 {$model['name']} (" . round($model['size']/1000000000, 1) . " GB)\n";
}
// 2. Usar un modelo espec铆fico (phi, mistral, etc.)
$respuesta = $ollama->generate(
"¿Cu谩l es la f贸rmula del 谩rea de un c铆rculo?",
"phi" // ← Puedes cambiar esto por cualquier modelo instalado
);
echo "\n馃摑 Respuesta de Phi:\n" . $respuesta['response'] . "\n";
// 3. Chat con historial
$historial = [
["role" => "system", "content" => "Eres un tutor de matem谩ticas paciente y claro."],
["role" => "user", "content" => "¿Qu茅 es el teorema de Pit谩goras?"]
];
$chatRespuesta = $ollama->chat($historial, "mistral");
echo "\n馃М Tutor (Mistral):\n" . $chatRespuesta['message']['content'] . "\n";
// 4. Streaming en tiempo real
echo "\n馃幆 Streaming (escribiendo en tiempo real):\n";
$ollama->generateStream(
"Cuenta una historia corta sobre un drag贸n",
function($texto, $finalizado) {
echo $texto;
if ($finalizado) {
echo "\n--- Fin ---\n";
}
},
"llama3.2"
);
?>馃寪 Ejemplo completo: Aplicaci贸n web PHP con Ollama
<?php
// index.php - Interfaz web simple
session_start();
class OllamaWeb {
private $client;
public function __construct() {
$this->client = new OllamaClient();
}
public function handleRequest() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
switch ($action) {
case 'list_models':
$this->listModels();
break;
case 'send_message':
$this->processMessage();
break;
case 'clear_chat':
$_SESSION['chat_history'] = [];
echo json_encode(['status' => 'success']);
break;
}
} else {
$this->showInterface();
}
}
private function listModels() {
try {
$models = $this->client->listModels();
echo json_encode([
'status' => 'success',
'models' => $models['models']
]);
} catch (Exception $e) {
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}
}
private function processMessage() {
$message = $_POST['message'] ?? '';
$model = $_POST['model'] ?? 'llama3.2';
if (empty($message)) {
echo json_encode(['status' => 'error', 'message' => 'Mensaje vac铆o']);
return;
}
// Inicializar historial si no existe
if (!isset($_SESSION['chat_history'])) {
$_SESSION['chat_history'] = [];
}
// Agregar mensaje del usuario al historial
$_SESSION['chat_history'][] = [
'role' => 'user',
'content' => $message,
'timestamp' => date('H:i:s')
];
try {
// Convertir historial de sesi贸n al formato de Ollama
$messages = [];
foreach ($_SESSION['chat_history'] as $msg) {
$messages[] = [
'role' => $msg['role'],
'content' => $msg['content']
];
}
// Enviar a Ollama
$response = $this->client->chat($messages, $model);
// Agregar respuesta al historial
$_SESSION['chat_history'][] = [
'role' => 'assistant',
'content' => $response['message']['content'],
'timestamp' => date('H:i:s'),
'model' => $model
];
echo json_encode([
'status' => 'success',
'response' => $response['message']['content'],
'history' => $_SESSION['chat_history']
]);
} catch (Exception $e) {
echo json_encode([
'status' => 'error',
'message' => 'Error: ' . $e->getMessage()
]);
}
}
private function showInterface() {
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat con Ollama</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
#chat-container { border: 1px solid #ccc; height: 400px; overflow-y: auto; padding: 10px; margin-bottom: 10px; }
.message { margin-bottom: 10px; padding: 8px; border-radius: 5px; }
.user { background-color: #e3f2fd; text-align: right; }
.assistant { background-color: #f1f8e9; }
.timestamp { font-size: 0.8em; color: #666; }
#model-select { padding: 5px; margin-bottom: 10px; }
</style>
</head>
<body>
<h1>馃挰 Chat con Ollama</h1>
<select id="model-select">
<option value="llama3.2">Llama 3.2</option>
<option value="mistral">Mistral</option>
<option value="phi">Phi</option>
<option value="gemma:2b">Gemma 2B</option>
</select>
<button onclick="loadModels()">馃攧 Cargar Modelos</button>
<button onclick="clearChat()">馃Ч Limpiar Chat</button>
<div id="chat-container"></div>
<div>
<input type="text" id="message-input" placeholder="Escribe tu mensaje..." style="width: 70%; padding: 10px;">
<button onclick="sendMessage()" style="padding: 10px;">Enviar</button>
</div>
<script>
let chatHistory = <?php echo json_encode($_SESSION['chat_history'] ?? []); ?>;
function renderChat() {
const container = document.getElementById('chat-container');
container.innerHTML = '';
chatHistory.forEach(msg => {
const div = document.createElement('div');
div.className = `message ${msg.role}`;
div.innerHTML = `
<div><strong>${msg.role === 'user' ? '馃懁 T煤' : '馃 Asistente'}</strong></div>
<div>${msg.content}</div>
<div class="timestamp">${msg.timestamp} ${msg.model ? `(Modelo: ${msg.model})` : ''}</div>
`;
container.appendChild(div);
});
container.scrollTop = container.scrollHeight;
}
function sendMessage() {
const input = document.getElementById('message-input');
const message = input.value.trim();
const model = document.getElementById('model-select').value;
if (!message) return;
// Agregar mensaje temporal
chatHistory.push({
role: 'user',
content: message,
timestamp: new Date().toLocaleTimeString()
});
renderChat();
// Enviar al servidor
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: `action=send_message&message=${encodeURIComponent(message)}&model=${model}`
})
.then(r => r.json())
.then(data => {
if (data.status === 'success') {
chatHistory = data.history;
renderChat();
input.value = '';
} else {
alert('Error: ' + data.message);
}
});
}
function loadModels() {
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=list_models'
})
.then(r => r.json())
.then(data => {
if (data.status === 'success') {
const select = document.getElementById('model-select');
select.innerHTML = '';
data.models.forEach(model => {
const option = document.createElement('option');
option.value = model.name;
option.textContent = model.name;
select.appendChild(option);
});
alert(`✅ ${data.models.length} modelos cargados`);
}
});
}
function clearChat() {
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=clear_chat'
})
.then(r => r.json())
.then(data => {
if (data.status === 'success') {
chatHistory = [];
renderChat();
}
});
}
// Enter para enviar
document.getElementById('message-input').addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});
// Renderizar chat inicial
renderChat();
</script>
</body>
</html>
<?php
}
}
// Incluir la clase OllamaClient aqu铆 (del ejemplo anterior)
// Ejecutar la aplicaci贸n
$app = new OllamaWeb();
$app->handleRequest();
?>馃敡 Configuraci贸n avanzada y ejemplos
1. Par谩metros de generaci贸n
// Ejemplo con todos los par谩metros
$respuesta = $ollama->generate("Escribe un poema", "llama3.2", [
'stream' => false,
'options' => [
'temperature' => 0.8, // Creatividad (0.0-1.0)
'num_predict' => 1000, // M谩ximo de tokens
'top_k' => 40, // Diversidad
'top_p' => 0.9, // Probabilidad acumulada
'repeat_penalty' => 1.1, // Penalizar repeticiones
'seed' => 42 // Semilla para reproducibilidad
]
]);2. M煤ltiples modelos en paralelo
// Probar diferentes modelos con el mismo prompt
function comparar_modelos($prompt) {
$modelos = ['llama3.2', 'mistral', 'phi'];
$resultados = [];
foreach ($modelos as $modelo) {
try {
$respuesta = $ollama->generate($prompt, $modelo);
$resultados[$modelo] = [
'respuesta' => $respuesta['response'],
'tiempo' => $respuesta['total_duration'] ?? 'N/A'
];
} catch (Exception $e) {
$resultados[$modelo] = ['error' => $e->getMessage()];
}
}
return $resultados;
}3. Sistema distribuido
// Conectar a Ollama en otra m谩quina
$ollama_remoto = new OllamaClient('http://192.168.1.100:11434');
// O con autenticaci贸n b谩sica
$ollama_seguro = new OllamaClient('http://usuario:contrase帽a@servidor:11434');❓ Preguntas frecuentes resueltas
¿Puedo usar cualquier modelo instalado con la API?
✅ S铆, absolutamente. Todos los modelos que ves con ollama list est谩n disponibles autom谩ticamente en http://localhost:11434/api/generate y http://localhost:11434/api/chat.
¿La API es compatible con OpenAI?
✅ Parcialmente s铆. Los endpoints /api/chat usan un formato similar, pero no id茅ntico. Sin embargo, existen bibliotecas que act煤an como puente:
# Usar Ollama como drop-in replacement para OpenAI
curl http://localhost:11434/v1/chat/completions -d '{
"model": "llama3.2",
"messages": [{"role": "user", "content": "Hola"}]
}'¿C贸mo manejar respuestas largas?
// Usar streaming para respuestas largas
$ollama->generateStream(
"Escribe un ensayo de 500 palabras sobre inteligencia artificial",
function($chunk, $done) {
echo $chunk;
flush(); // Importante para output inmediato
if ($done) {
echo "\n\n✅ Completado\n";
}
}
);¿Qu茅 puertos usa?
11434: Puerto principal HTTP
11435: Puerto para WebSocket (streaming)
¿C贸mo ver logs de la API?
# Iniciar Ollama en modo verbose
ollama serve --verbose
# Ver logs en tiempo real
tail -f ~/.ollama/logs/server.log馃殌 Ejemplo final: API completa en un solo archivo
Guarda esto como ollama_api.php:
<?php
/**
* Archivo 煤nico para probar toda la API de Ollama
* Uso: php ollama_api.php [comando] [argumentos]
*/
class OllamaQuickTest {
public static function run($command) {
switch ($command) {
case 'list':
self::listModels();
break;
case 'test':
self::testGenerate();
break;
case 'chat':
self::testChat();
break;
case 'stream':
self::testStream();
break;
default:
echo "Comandos disponibles:\n";
echo " php ollama_api.php list # Listar modelos\n";
echo " php ollama_api.php test # Probar generaci贸n\n";
echo " php ollama_api.php chat # Probar chat\n";
echo " php ollama_api.php stream # Probar streaming\n";
}
}
private static function request($endpoint, $data = null) {
$ch = curl_init('http://localhost:11434' . $endpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30
]);
if ($data) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
private static function listModels() {
echo "馃摝 Modelos disponibles:\n";
$result = self::request('/api/tags');
foreach ($result['models'] as $model) {
echo " • {$model['name']}\n";
}
}
private static function testGenerate() {
echo "馃И Probando generaci贸n con llama3.2...\n";
$result = self::request('/api/generate', [
'model' => 'llama3.2',
'prompt' => 'Explica qu茅 es la API REST en una frase',
'stream' => false
]);
echo "✅ Respuesta:\n";
echo $result['response'] . "\n";
echo "Tiempo: " . ($result['total_duration'] / 1000000000) . "s\n";
}
private static function testChat() {
echo "馃挰 Probando chat...\n";
$result = self::request('/api/chat', [
'model' => 'llama3.2',
'messages' => [
['role' => 'user', 'content' => 'Hola, ¿cu谩l es tu nombre?']
],
'stream' => false
]);
echo "馃: " . $result['message']['content'] . "\n";
}
private static function testStream() {
echo "馃寠 Probando streaming...\n";
$url = 'http://localhost:11434/api/generate';
$data = [
'model' => 'llama3.2',
'prompt' => 'Cuenta del 1 al 10',
'stream' => true
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_WRITEFUNCTION => function($ch, $data) {
$lines = explode("\n", $data);
foreach ($lines as $line) {
$line = trim($line);
if (!empty($line) && strpos($line, 'data: ') === 0) {
$json = substr($line, 6);
if ($json !== '[DONE]') {
$decoded = json_decode($json, true);
if ($decoded && isset($decoded['response'])) {
echo $decoded['response'];
}
}
}
}
return strlen($data);
}
]);
curl_exec($ch);
curl_close($ch);
echo "\n✅ Streaming completado\n";
}
}
// Ejecutar desde l铆nea de comandos
if (php_sapi_name() === 'cli') {
$command = $argv[1] ?? 'help';
OllamaQuickTest::run($command);
}
?>馃搳 Resumen final
✅ La API REST est谩 siempre activa cuando Ollama corre
✅ Todos los modelos instalados son accesibles por API
✅ PHP se integra f谩cilmente con cURL o Guzzle
✅ Soporta streaming para respuestas en tiempo real
✅ Es compatible con el formato de mensajes de OpenAI
✅ Funciona en red (puedes servir a otras m谩quinas)
Para probar inmediatamente:
# 1. Inicia Ollama
ollama serve
# 2. En otra terminal, prueba
curl http://localhost:11434/api/tags
# 3. Con PHP
php -r "echo file_get_contents('http://localhost:11434/api/tags');"¡Ya tienes todo lo necesario para integrar Ollama en tus aplicaciones PHP
Comentarios
Publicar un comentario