Compare commits

..
Author SHA1 Message Date
hermes ec4eff61ab feat: estilo editorial de la IA configurable (editorial-style.php)
La IA del plugin 'AI' redacta segun el estilo del pack: editorial-style.php
(tono, normas, prohibidos, max chars de titulares) se copia a wp-content y un
mu-plugin lo inyecta en las instrucciones de sistema via wpai_system_instruction
(scp: wpai_title_generation_system_instruction para el cap de caracteres).
Editas el fichero, re-ejecutas deploy y la IA cambia de estilo.
2026-09-13 00:10:55 +02:00
hermes dbbac84b0d feat: pack wordpress con IA nativa de WP 7.1 — fuera Open WebUI
- Eliminado el servicio openwebui del pack wordpress (no estaba integrado
  con WP; el chat IA vive ahora dentro del editor de WordPress).
- Plugins IA oficiales preinstalados y configurados en el arranque:
  'AI' (AI Experiments) + 'AI Provider for Ollama' (connector), host
  http://ollama:11434 (red interna del compose, sin puerto publicado).
- Autoconfig via contenedor oficial wordpress:cli (--volumes-from):
  espera a core is-installed, instala plugins y setea el option. Si WP
  aun no esta instalado, aviso y re-ejecucion tras el asistente.
- CPU cap dinamico en hermes/openclaw (min 6, nproc) y OLLAMA_CPUS
  parametrizado en compose (6.0 default) — evita fallo en hosts de 4 cores.
- .gitignore raiz (**/.env, *.gguf, override GPU).
- GATE A PASS: subagente independiente (5 findings aplicados: EAP 5.1,
  .gitignore, fallo autoconfig visible, red vacia, indentacion).
- E2E verificado en VPS: plugins activos + option host + provider
  responde /v1/chat/completions del modelo local.
2026-09-12 23:48:19 +02:00
hermes 9438b75c9f fix: limite de CPU 6.0 para ollama (#25)
Co-authored-by: Hermes Agent <hermes@d0a1.es>
2026-09-12 23:07:46 +02:00
hermes 5030770bc2 fix: think=false en Open WebUI (el SYSTEM /no_think no basta) (#24)
Co-authored-by: Hermes Agent <hermes@d0a1.es>
2026-09-12 23:06:35 +02:00
hermes c68fe80484 fix: desactivar el thinking de Qwen3 (SYSTEM /no_think) (#23)
Co-authored-by: Hermes Agent <hermes@d0a1.es>
2026-09-12 22:38:19 +02:00
12 changed files with 230 additions and 70 deletions

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
# .env files (contraseñas generadas en deploy) — nunca al repo
**/.env
!**/.env.example
# Overrides GPU generados en runtime
docker-compose.gpu.yml
# Modelos descargados temporalmente
*.gguf
+2 -1
View File
@@ -117,7 +117,8 @@ $running = docker ps --format '{{.Names}}' | Select-String -Quiet 'd0a1-hermes-o
if (-not $running) { if (-not $running) {
$GpuArgs = @() $GpuArgs = @()
if ($HasGpu) { $GpuArgs = @('--gpus', 'all') } if ($HasGpu) { $GpuArgs = @('--gpus', 'all') }
docker run -d --name d0a1-hermes-ollama --memory 6g @GpuArgs ` $CpuCap = [Math]::Min(6, [int]$env:NUMBER_OF_PROCESSORS)
docker run -d --name d0a1-hermes-ollama --memory 6g --cpus $CpuCap @GpuArgs `
-p "${OllamaPort}:11434" ` -p "${OllamaPort}:11434" `
-v d0a1_hermes_ollama:/root/.ollama ` -v d0a1_hermes_ollama:/root/.ollama `
-e OLLAMA_HOST=0.0.0.0 ` -e OLLAMA_HOST=0.0.0.0 `
+3 -1
View File
@@ -92,10 +92,12 @@ echo ""
# ── 1. Arrancar Ollama (Docker) ────────────── # ── 1. Arrancar Ollama (Docker) ──────────────
echo -e "${YELLOW}[1/3]${NC} Arrancando Ollama (IA local)..." echo -e "${YELLOW}[1/3]${NC} Arrancando Ollama (IA local)..."
if ! docker ps --format '{{.Names}}' | grep -q d0a1-hermes-ollama; then if ! docker ps --format '{{.Names}}' | grep -q d0a1-hermes-ollama; then
CPU_CAP=$(( $(nproc) > 6 ? 6 : $(nproc) ))
docker run -d --name d0a1-hermes-ollama \ docker run -d --name d0a1-hermes-ollama \
-p "${OLLAMA_PORT}:11434" \ -p "${{OLLAMA_PORT}}:11434" \
-v d0a1_hermes_ollama:/root/.ollama \ -v d0a1_hermes_ollama:/root/.ollama \
-e OLLAMA_HOST=0.0.0.0 \ -e OLLAMA_HOST=0.0.0.0 \
--memory 6g --cpus "$CPU_CAP" \
--restart unless-stopped \ --restart unless-stopped \
ollama/ollama:0.33.3 ollama/ollama:0.33.3
fi fi
+2 -1
View File
@@ -117,7 +117,8 @@ $running = docker ps --format '{{.Names}}' | Select-String -Quiet 'd0a1-openclaw
if (-not $running) { if (-not $running) {
$GpuArgs = @() $GpuArgs = @()
if ($HasGpu) { $GpuArgs = @('--gpus', 'all') } if ($HasGpu) { $GpuArgs = @('--gpus', 'all') }
docker run -d --name d0a1-openclaw-ollama --memory 6g @GpuArgs ` $CpuCap = [Math]::Min(6, [int]$env:NUMBER_OF_PROCESSORS)
docker run -d --name d0a1-openclaw-ollama --memory 6g --cpus $CpuCap @GpuArgs `
-p "${OllamaPort}:11434" ` -p "${OllamaPort}:11434" `
-v d0a1_openclaw_ollama:/root/.ollama ` -v d0a1_openclaw_ollama:/root/.ollama `
-e OLLAMA_HOST=0.0.0.0 ` -e OLLAMA_HOST=0.0.0.0 `
+3 -1
View File
@@ -92,10 +92,12 @@ echo ""
# ── 1. Arrancar Ollama (Docker) ────────────── # ── 1. Arrancar Ollama (Docker) ──────────────
echo -e "${YELLOW}[1/3]${NC} Arrancando Ollama (IA local)..." echo -e "${YELLOW}[1/3]${NC} Arrancando Ollama (IA local)..."
if ! docker ps --format '{{.Names}}' | grep -q d0a1-openclaw-ollama; then if ! docker ps --format '{{.Names}}' | grep -q d0a1-openclaw-ollama; then
CPU_CAP=$(( $(nproc) > 6 ? 6 : $(nproc) ))
docker run -d --name d0a1-openclaw-ollama \ docker run -d --name d0a1-openclaw-ollama \
-p "${OLLAMA_PORT}:11434" \ -p "${{OLLAMA_PORT}}:11434" \
-v d0a1_openclaw_ollama:/root/.ollama \ -v d0a1_openclaw_ollama:/root/.ollama \
-e OLLAMA_HOST=0.0.0.0 \ -e OLLAMA_HOST=0.0.0.0 \
--memory 6g --cpus "$CPU_CAP" \
--restart unless-stopped \ --restart unless-stopped \
ollama/ollama:0.33.3 ollama/ollama:0.33.3
fi fi
+2 -1
View File
@@ -30,6 +30,7 @@ services:
resources: resources:
limits: limits:
memory: 6G memory: 6G
cpus: "${OLLAMA_CPUS:-6.0}"
openwebui: openwebui:
container_name: d0a1-openwebui container_name: d0a1-openwebui
@@ -48,7 +49,7 @@ services:
ENABLE_WEB_SEARCH: "false" ENABLE_WEB_SEARCH: "false"
ENABLE_DIRECT_CONNECTIONS: "false" ENABLE_DIRECT_CONNECTIONS: "false"
CHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS: "1" CHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS: "1"
DEFAULT_MODEL_PARAMS: '{"function_calling": "legacy"}' DEFAULT_MODEL_PARAMS: '{"function_calling": "legacy", "think": false}'
depends_on: depends_on:
ollama: ollama:
condition: service_healthy condition: service_healthy
+7 -6
View File
@@ -1,10 +1,10 @@
# WordPress + IA — WP con IA local # WordPress + IA — WP con IA local
Tu WordPress con IA local conectada. Genera borradores, modera comentarios y chatea con tu sitio. Tu WordPress con IA local conectada. Titulares, extractos y moderación de comentarios con IA, dentro del propio editor.
## Qué es ## Qué es
WordPress corriendo en tu servidor con Ollama (IA local) y Open WebUI conectados. Instala el plugin AI Agent Bridge para generar contenido y moderar comentarios con IA local, sin API de terceros. WordPress corriendo en tu servidor con Ollama (IA local) conectado de serie: los plugins **AI** y **AI Provider for Ollama** se instalan y configuran solos durante el deploy. Genera títulos, extractos y alt text desde el editor de WordPress con tu propio modelo (qwen3:4b), sin API de terceros ni cuotas.
## Prueba local (gratis, 1 comando) ## Prueba local (gratis, 1 comando)
@@ -13,9 +13,8 @@ WordPress corriendo en tu servidor con Ollama (IA local) y Open WebUI conectados
``` ```
- WordPress: http://localhost:8080 - WordPress: http://localhost:8080
- IA (chat): http://localhost:3000
Sigue el asistente de instalación de WordPress (2 minutos), luego instala el plugin AI Agent Bridge desde el panel. Sigue el asistente de instalación de WordPress (2 minutos). Al terminar, ejecuta `./deploy.sh` otra vez y los plugins de IA quedan activados y conectados.
## Despliega en VPS ## Despliega en VPS
@@ -33,13 +32,14 @@ Solo pagas el servidor. WordPress y la IA son tuyos.
- Docker instalado - Docker instalado
- 6 GB RAM mínimo - 6 GB RAM mínimo
- GPU NVIDIA recomendada (detección automática)
## Qué incluye ## Qué incluye
- WordPress 7.0 + PHP 8.4 - WordPress 7.1 + PHP 8.4
- MariaDB 11.8 - MariaDB 11.8
- Ollama (IA local, modelo qwen3:4b por defecto) - Ollama (IA local, modelo qwen3:4b por defecto)
- Open WebUI (interfaz de chat) - Plugins IA preconfigurados: AI (funciones en el editor) + AI Provider for Ollama
## Comandos ## Comandos
@@ -47,4 +47,5 @@ Solo pagas el servidor. WordPress y la IA son tuyos.
./deploy.sh local # Arrancar en local ./deploy.sh local # Arrancar en local
./deploy.sh vps # Arrancar en VPS ./deploy.sh vps # Arrancar en VPS
./deploy.sh stop # Parar todo ./deploy.sh stop # Parar todo
./remove.sh # Desinstalar todo (contenedores + datos)
``` ```
+77 -15
View File
@@ -137,15 +137,13 @@ if (-not $HasGpu) {
cmd /c docker compose -f $ComposeFile down '>' NUL '2>&1' cmd /c docker compose -f $ComposeFile down '>' NUL '2>&1'
Info 'Deploy previo detenido (down; volumenes con el modelo intactos).' Info 'Deploy previo detenido (down; volumenes con el modelo intactos).'
$OllamaPort = Find-FreePort 11434 $OllamaPort = $null # Ollama ya no publica puerto: solo lo usa WP por la red interna
$WpPort = Find-FreePort 8080 $WpPort = Find-FreePort 8080
$WebUiPort = Find-FreePort 3000
# --- Mode info ------------------------------------------------------------ # --- Mode info ------------------------------------------------------------
if ($Mode -eq 'local') { if ($Mode -eq 'local') {
Warn 'Modo: local (tu ordenador)' Warn 'Modo: local (tu ordenador)'
Write-Host " WordPress: ${Cyan}http://localhost:${WpPort}${NC}" Write-Host " WordPress: ${Cyan}http://localhost:${WpPort}${NC}"
Write-Host " IA (chat): ${Cyan}http://localhost:${WebUiPort}${NC}"
} else { } else {
Warn 'Modo: VPS (accesible desde fuera)' Warn 'Modo: VPS (accesible desde fuera)'
# IP real de LAN: la de la interfaz con la ruta por defecto (evita # IP real de LAN: la de la interfaz con la ruta por defecto (evita
@@ -157,24 +155,19 @@ if ($Mode -eq 'local') {
Select-Object -First 1).IPAddress Select-Object -First 1).IPAddress
if (-not $Ip) { $Ip = 'TU_IP' } if (-not $Ip) { $Ip = 'TU_IP' }
Write-Host " WordPress: ${Cyan}http://${Ip}:${WpPort}${NC}" Write-Host " WordPress: ${Cyan}http://${Ip}:${WpPort}${NC}"
Write-Host " IA (chat): ${Cyan}http://${Ip}:${WebUiPort}${NC}"
Write-Host '' Write-Host ''
Write-Host " ${Bold}¿No tienes VPS?${NC} Consigue uno con descuento:" Write-Host " ${Bold}¿No tienes VPS?${NC} Consigue uno con descuento:"
Write-Host " ${Cyan}https://d0a1.es/go/vultr/${NC} - Vultr" Write-Host " ${Cyan}https://d0a1.es/go/vultr/${NC} - Vultr"
Write-Host " ${Cyan}https://d0a1.es/go/digitalocean/${NC} - DigitalOcean (`$200 credit)" Write-Host " ${Cyan}https://d0a1.es/go/digitalocean/${NC} - DigitalOcean (`$200 credit)"
} }
if ($OllamaPort -ne 11434) { Warn "Puerto 11434 ocupado, usando $OllamaPort" } if ($WpPort -ne 8080) { Warn "Puerto 8080 ocupado, usando $WpPort" }
if ($WpPort -ne 8080) { Warn "Puerto 8080 ocupado, usando $WpPort" }
if ($WebUiPort -ne 3000) { Warn "Puerto 3000 ocupado, usando $WebUiPort" }
Write-Host '' Write-Host ''
# --- Deploy ---------------------------------------------------------------- # --- Deploy ----------------------------------------------------------------
Warn 'Descargando contenedores (3-5 min la primera vez)...' Warn 'Descargando contenedores (3-5 min la primera vez)...'
cmd /c docker compose -f $ComposeFile down '>' NUL '2>&1' cmd /c docker compose -f $ComposeFile down '>' NUL '2>&1'
$env:OLLAMA_PORT = "$OllamaPort"
$env:WP_PORT = "$WpPort" $env:WP_PORT = "$WpPort"
$env:WEBUI_PORT = "$WebUiPort"
$ComposeArgs = @('-f', $ComposeFile) $ComposeArgs = @('-f', $ComposeFile)
if ($HasGpu) { if ($HasGpu) {
$OverrideFile = Join-Path $ScriptDir 'docker-compose.gpu.yml' $OverrideFile = Join-Path $ScriptDir 'docker-compose.gpu.yml'
@@ -258,19 +251,88 @@ cmd /c docker exec d0a1-wp-ollama sh -c 'ollama run qwen3:4b ""' '>' NUL '2>&1'
# --- Done ------------------------------------------------------------------- # --- Done -------------------------------------------------------------------
Write-Host '' Write-Host ''
Info 'Listo! Tu WordPress con IA esta corriendo.' Info 'Listo! Tu WordPress con IA esta corriendo.'
Write-Host ''
# --- Auto-config WordPress: plugins IA + conexion a Ollama -------------------
# EAP=Continue: los 2>$null sobre nativos con EAP=Stop provocan NativeCommandError
# terminante (PS #4002, ver nota arriba). El bloque nuevo maneja errores por rc.
$PrevEap = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
# El contenedor wordpress:7.1 no trae WP-CLI; usar el contenedor wordpress:cli.
Info 'Configurando los plugins de IA en WordPress (1-2 min la primera vez)...'
cmd /c "docker pull wordpress:cli >NUL 2>&1"
# Red real del compose (el nombre depende del directorio, no es fijo):
$WpNet = docker inspect d0a1-wp --format '{{range $k,$_ := .NetworkSettings.Networks}}{{$k}}{{end}}' 2>$null
if (-not $WpNet) {
Warn 'No se detecto la red del compose (d0a1-wp no corre). Saltando autoconfig.'
$ErrorActionPreference = $PrevEap
exit 1
}
$EnvFileContent = Get-Content $EnvFile -ErrorAction SilentlyContinue
$WpDbPass = ($EnvFileContent | Where-Object { $_ -like 'MYSQL_PASSWORD=*' }) -replace '^MYSQL_PASSWORD=', '' | Select-Object -First 1
if (-not $WpDbPass) { $WpDbPass = 'change-me-to-a-strong-password' }
function Invoke-WpCli {
# docker run con --volumes-from (mismo patron env que usa el compose).
$ArgsList = @('run', '--rm', '--network', $WpNet, '--volumes-from', 'd0a1-wp',
'--env', "WORDPRESS_DB_HOST=db:3306",
'--env', 'WORDPRESS_DB_NAME=wordpress',
'--env', 'WORDPRESS_DB_USER=wordpress',
'--env', "WORDPRESS_DB_PASSWORD=$WpDbPass",
'wordpress:cli') + $Args + @('--allow-root')
& docker @ArgsList 2>$null
return $LASTEXITCODE
}
# Esperar a que WP este instalado (asistente del navegador opcional: si el
# usuario aun no instalo WP, la autoconfig se salta y puede repetir deploy).
$WpReady = $false
for ($i = 0; $i -lt 15; $i++) {
if ((Invoke-WpCli 'core' 'is-installed') -eq 0) { $WpReady = $true; break }
Start-Sleep -Seconds 4
}
if ($WpReady) {
$Rc = Invoke-WpCli 'plugin' 'install' 'ai' 'ai-provider-for-ollama' '--activate'
if ($Rc -eq 0) {
$Rc = Invoke-WpCli 'option' 'update' 'ai_provider_for_ollama_settings' '{"host":"http://ollama:11434"}' '--format=json'
}
if ($Rc -eq 0) {
Ok 'Plugins de IA activados y conectados a Ollama (IA local)'
} else {
Warn 'No se pudo auto-configurar. Re-ejecuta .\deploy.ps1 tras el asistente de WordPress.'
}
# Estilo editorial: copiar al wp-content (editable: editorial-style.php del pack)
# e instalar el mu-plugin que lo inyecta en las instrucciones de la IA.
$StyleRc = 1
cmd /c "docker exec d0a1-wp mkdir -p /var/www/html/wp-content/mu-plugins >NUL 2>&1"
if ($LASTEXITCODE -eq 0) {
docker cp (Join-Path $ScriptDir 'editorial-style.php') d0a1-wp:/var/www/html/wp-content/editorial-style.php 2>$null
if ($LASTEXITCODE -eq 0) {
docker cp (Join-Path $ScriptDir 'mu-plugin-d0a1-editorial.php') d0a1-wp:/var/www/html/wp-content/mu-plugins/d0a1-editorial.php 2>$null
$StyleRc = $LASTEXITCODE
}
}
if ($StyleRc -eq 0) {
Ok 'Estilo editorial activo (edita editorial-style.php y re-ejecuta para cambiarlo)'
}
} else {
Warn 'WordPress aun no esta instalado. Tras el asistente del navegador,'
Warn 'vuelve a ejecutar .\deploy.ps1 y los plugins de IA se configuran solos.'
}
$ErrorActionPreference = $PrevEap
Write-Host '' Write-Host ''
if ($Mode -eq 'local') { if ($Mode -eq 'local') {
Write-Host " ${Bold}WordPress:${NC} ${Cyan}http://localhost:${WpPort}${NC}" Write-Host " ${Bold}WordPress:${NC} ${Cyan}http://localhost:${WpPort}${NC}"
Write-Host " ${Bold}IA (chat):${NC} ${Cyan}http://localhost:${WebUiPort}${NC}"
} else { } else {
Write-Host " ${Bold}WordPress:${NC} ${Cyan}http://${Ip}:${WpPort}${NC}" Write-Host " ${Bold}WordPress:${NC} ${Cyan}http://${Ip}:${WpPort}${NC}"
Write-Host " ${Bold}IA (chat):${NC} ${Cyan}http://${Ip}:${WebUiPort}${NC}"
} }
Write-Host '' Write-Host ''
Write-Host " ${Bold}Configurar WordPress:${NC}" Write-Host " ${Bold}IA integrada en el editor:${NC} titulos sugeridos, extractos, alt text"
Write-Host ' 1. Abre la URL de WordPress en tu navegador' Write-Host ' y moderacion de comentarios con tu modelo local (qwen3:4b).'
Write-Host ' 2. Sigue el asistente de instalacion (2 minutos)' Write-Host " Configuracion en ${Bold}Ajustes - Connectors${NC} dentro de WordPress."
Write-Host " 3. Instala el plugin 'AI Agent Bridge' desde el panel"
Write-Host '' Write-Host ''
Write-Host " ${Bold}Parar:${NC} .\deploy.ps1 stop" Write-Host " ${Bold}Parar:${NC} .\deploy.ps1 stop"
Write-Host " ${Bold}¿Quieres mas?${NC} ${Cyan}https://d0a1.es/packs/${NC}" Write-Host " ${Bold}¿Quieres mas?${NC} ${Cyan}https://d0a1.es/packs/${NC}"
+57 -16
View File
@@ -84,20 +84,16 @@ find_free_port() {
return 1 return 1
} }
export OLLAMA_PORT=$(find_free_port 11434)
export WP_PORT=$(find_free_port 8080) export WP_PORT=$(find_free_port 8080)
export WEBUI_PORT=$(find_free_port 3000)
# ── Mode info ─────────────────────────────── # ── Mode info ───────────────────────────────
if [ "$MODE" = "local" ]; then if [ "$MODE" = "local" ]; then
echo -e "${YELLOW}Modo:${NC} local (tu ordenador)" echo -e "${YELLOW}Modo:${NC} local (tu ordenador)"
echo -e " WordPress: ${CYAN}http://localhost:${WP_PORT}${NC}" echo -e " WordPress: ${CYAN}http://localhost:${WP_PORT}${NC}"
echo -e " IA (chat): ${CYAN}http://localhost:${WEBUI_PORT}${NC}"
elif [ "$MODE" = "vps" ]; then elif [ "$MODE" = "vps" ]; then
IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'TU_IP') IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'TU_IP')
echo -e "${YELLOW}Modo:${NC} VPS (accesible desde fuera)" echo -e "${YELLOW}Modo:${NC} VPS (accesible desde fuera)"
echo -e " WordPress: ${CYAN}http://${IP}:${WP_PORT}${NC}" echo -e " WordPress: ${CYAN}http://${IP}:${WP_PORT}${NC}"
echo -e " IA (chat): ${CYAN}http://${IP}:${WEBUI_PORT}${NC}"
echo "" echo ""
echo -e " ${BOLD}¿No tienes VPS?${NC} Consigue uno con descuento:" echo -e " ${BOLD}¿No tienes VPS?${NC} Consigue uno con descuento:"
echo -e " ${CYAN}https://d0a1.es/go/vultr/${NC} — Vultr" echo -e " ${CYAN}https://d0a1.es/go/vultr/${NC} — Vultr"
@@ -108,15 +104,9 @@ else
exit 1 exit 1
fi fi
if [ "$OLLAMA_PORT" != "11434" ]; then
echo -e "${YELLOW}⚠ Puerto 11434 ocupado, usando ${OLLAMA_PORT}${NC}"
fi
if [ "$WP_PORT" != "8080" ]; then if [ "$WP_PORT" != "8080" ]; then
echo -e "${YELLOW}⚠ Puerto 8080 ocupado, usando ${WP_PORT}${NC}" echo -e "${YELLOW}⚠ Puerto 8080 ocupado, usando ${WP_PORT}${NC}"
fi fi
if [ "$WEBUI_PORT" != "3000" ]; then
echo -e "${YELLOW}⚠ Puerto 3000 ocupado, usando ${WEBUI_PORT}${NC}"
fi
echo "" echo ""
# ── Deploy ────────────────────────────────── # ── Deploy ──────────────────────────────────
@@ -140,19 +130,70 @@ docker exec d0a1-wp-ollama ollama pull qwen3:4b
# ── Done ──────────────────────────────────── # ── Done ────────────────────────────────────
echo "" echo ""
echo -e "${BOLD}${GREEN}¡Listo! Tu WordPress con IA está corriendo.${NC}" echo -e "${BOLD}${GREEN}¡Listo! Tu WordPress con IA está corriendo.${NC}"
echo ""
# ── Auto-config WordPress (plugins IA + conexión a Ollama) ──
# El contenedor wordpress:7.1 no trae WP-CLI; usar el contenedor wordpress:cli.
echo -e "${YELLOW}Configurando los plugins de IA en WordPress (1-2 min la primera vez)...${NC}"
docker pull wordpress:cli >/dev/null 2>&1
# Red real del compose (el nombre depende del directorio, no es fijo):
WP_NET=$(docker inspect d0a1-wp --format '{{range $k,$_ := .NetworkSettings.Networks}}{{$k}}{{end}}' 2>/dev/null)
if [ -z "$WP_NET" ]; then
echo -e "${YELLOW}⚠ No se detecto la red del compose (d0a1-wp no corre). Saltando autoconfig.${NC}"
exit 1
fi
WP_DB_PASS=$(grep '^MYSQL_PASSWORD=' "$SCRIPT_DIR/.env" | cut -d= -f2)
wpcli() {
docker run --rm --network "$WP_NET" --volumes-from d0a1-wp \
--env WORDPRESS_DB_HOST=db:3306 \
--env WORDPRESS_DB_NAME=wordpress \
--env WORDPRESS_DB_USER=wordpress \
--env WORDPRESS_DB_PASSWORD="$WP_DB_PASS" \
wordpress:cli "$@" --allow-root
}
# Esperar a que WP esté instalado (el asistente del navegador es opcional:
# si el usuario aun no instalo WP, la autoconfig se salta y se puede repetir
# re-ejecutando deploy.sh).
WP_READY=0
for i in $(seq 1 15); do
if wpcli core is-installed >/dev/null 2>&1; then
WP_READY=1
break
fi
sleep 4
done
if [ "$WP_READY" = "1" ]; then
wpcli plugin install ai ai-provider-for-ollama --activate >/dev/null 2>&1 \
&& wpcli option update ai_provider_for_ollama_settings \
'{"host":"http://ollama:11434"}' --format=json >/dev/null 2>&1 \
&& echo -e "${GREEN}✓${NC} Plugins de IA activados y conectados a Ollama (IA local)" \
|| echo -e "${YELLOW}⚠ No se pudo auto-configurar. Re-ejecuta ./deploy.sh tras el asistente de WordPress.${NC}"
# Estilo editorial: copiar al wp-content (editable: editorial-style.php del pack)
# e instalar el mu-plugin que lo inyecta en las instrucciones de la IA.
if docker cp "$SCRIPT_DIR/editorial-style.php" d0a1-wp:/var/www/html/wp-content/editorial-style.php >/dev/null 2>&1 \
&& docker exec d0a1-wp mkdir -p /var/www/html/wp-content/mu-plugins \
&& docker cp "$SCRIPT_DIR/mu-plugin-d0a1-editorial.php" \
d0a1-wp:/var/www/html/wp-content/mu-plugins/d0a1-editorial.php >/dev/null 2>&1; then
echo -e "${GREEN}✓${NC} Estilo editorial activo (edita editorial-style.php y re-ejecuta para cambiarlo)"
fi
else
echo -e "${YELLOW}⚠ WordPress aun no está instalado. Tras el asistente del navegador,"
echo -e " vuelve a ejecutar ./deploy.sh y los plugins de IA se configuran solos.${NC}"
fi
echo "" echo ""
if [ "$MODE" = "local" ]; then if [ "$MODE" = "local" ]; then
echo -e " ${BOLD}WordPress:${NC} ${CYAN}http://localhost:${WP_PORT}${NC}" echo -e " ${BOLD}WordPress:${NC} ${CYAN}http://localhost:${WP_PORT}${NC}"
echo -e " ${BOLD}IA (chat):${NC} ${CYAN}http://localhost:${WEBUI_PORT}${NC}"
else else
echo -e " ${BOLD}WordPress:${NC} ${CYAN}http://${IP}:${WP_PORT}${NC}" echo -e " ${BOLD}WordPress:${NC} ${CYAN}http://${IP}:${WP_PORT}${NC}"
echo -e " ${BOLD}IA (chat):${NC} ${CYAN}http://${IP}:${WEBUI_PORT}${NC}"
fi fi
echo "" echo ""
echo -e " ${BOLD}Configurar WordPress:${NC}" echo -e " ${BOLD}IA integrada en el editor:${NC} títulos sugeridos, extractos, alt text"
echo " 1. Abre la URL de WordPress en tu navegador" echo -e " y moderación de comentarios con tu modelo local (qwen3:4b)."
echo " 2. Sigue el asistente de instalación (2 minutos)" echo -e " Configuración en ${BOLD}Ajustes → Connectors${NC} dentro de WordPress."
echo " 3. Instala el plugin 'AI Agent Bridge' desde el panel"
echo "" echo ""
echo -e " ${BOLD}Parar:${NC} ./deploy.sh stop" echo -e " ${BOLD}Parar:${NC} ./deploy.sh stop"
echo -e " ${BOLD}¿Quieres más?${NC} ${CYAN}https://d0a1.es/packs/${NC}" echo -e " ${BOLD}¿Quieres más?${NC} ${CYAN}https://d0a1.es/packs/${NC}"
+5 -28
View File
@@ -5,7 +5,7 @@
# VPS: ./deploy.sh vps → http://TU_IP:8080 # VPS: ./deploy.sh vps → http://TU_IP:8080
# #
# Requisitos: Docker + 6 GB RAM mínimo # Requisitos: Docker + 6 GB RAM mínimo
# Incluye: WordPress + MariaDB + Ollama + Open WebUI # Incluye: WordPress + MariaDB + Ollama (IA local) + plugins IA (AI + AI Provider for Ollama)
services: services:
db: db:
container_name: d0a1-wp-db container_name: d0a1-wp-db
@@ -46,8 +46,9 @@ services:
container_name: d0a1-wp-ollama container_name: d0a1-wp-ollama
image: ollama/ollama:0.33.3 image: ollama/ollama:0.33.3
restart: unless-stopped restart: unless-stopped
ports: # Sin publicar puerto: solo lo usa WordPress (red interna del compose).
- "127.0.0.1:${OLLAMA_PORT:-11434}:11434" expose:
- "11434"
volumes: volumes:
- ollama_data:/root/.ollama - ollama_data:/root/.ollama
environment: environment:
@@ -63,29 +64,7 @@ services:
resources: resources:
limits: limits:
memory: 6G memory: 6G
cpus: "${OLLAMA_CPUS:-6.0}"
openwebui:
container_name: d0a1-wp-openwebui
image: ghcr.io/open-webui/open-webui:0.11.3
restart: unless-stopped
ports:
- "${WEBUI_PORT:-3000}:8080"
volumes:
- openwebui_data:/app/backend/data
environment:
OLLAMA_BASE_URL: "http://ollama:11434"
WEBUI_NAME: "d0a1"
WEBUI_AUTH: "false"
DEFAULT_LOCALE: "es"
CHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS: "1"
DEFAULT_MODEL_PARAMS: '{"function_calling": "legacy"}'
depends_on:
ollama:
condition: service_healthy
deploy:
resources:
limits:
memory: 2G
volumes: volumes:
wp_db: wp_db:
@@ -94,5 +73,3 @@ volumes:
driver: local driver: local
ollama_data: ollama_data:
driver: local driver: local
openwebui_data:
driver: local
+20
View File
@@ -0,0 +1,20 @@
<?php
/**
* Estilo editorial de d0a1 para la IA local (WordPress "AI" plugin).
* EDITA AQUI y re-ejecuta deploy para aplicarlo. La IA redacta segun esto.
*/
return array(
'estilo' => 'Directo, claro y cercano. Sin humo. Espanol de Espana (tu, no usted). Frases cortas.',
'reglas' => array(
'Spanish (Spain) siempre, salvo que el contenido fuente este en otro idioma.',
'Nada de relleno: cada frase aporta un dato, un beneficio o un paso.',
'Precios en euros (EUR).',
'Voz activa. Verbo al principio cuando sea natural.',
),
'prohibido' => array(
'Emojis, hashtags ni llamadas a la accion genericas.',
'Superlativos vacios ("increible", "revolucionario").',
'Markdown ni comillas envolviendo el resultado.',
),
'titulares_max_chars' => 60,
);
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
* Plugin Name: d0a1 — Instrucciones editoriales IA (mu-plugin)
* Description: Inyecta el estilo editorial (editorial-style.php del pack) en las instrucciones de sistema del plugin "AI".
*/
if ( ! defined( 'ABSPATH' ) ) { exit; }
function d0a1_editorial_style() {
$file = WP_CONTENT_DIR . '/editorial-style.php';
if ( ! file_exists( $file ) ) { return null; }
$s = include $file;
return is_array( $s ) ? $s : null;
}
function d0a1_editorial_block() {
$s = d0a1_editorial_style();
if ( ! $s ) { return ''; }
$out = "\n\nESTILO EDITORIAL DEL SITIO (prioridad sobre instrucciones anteriores):\n";
if ( ! empty( $s['estilo'] ) ) { $out .= 'Tono: ' . $s['estilo'] . "\n"; }
if ( ! empty( $s['reglas'] ) ) {
$out .= "Normas:\n";
foreach ( $s['reglas'] as $r ) { $out .= '- ' . $r . "\n"; }
}
if ( ! empty( $s['prohibido'] ) ) {
$out .= "Prohibido:\n";
foreach ( $s['prohibido'] as $p ) { $out .= '- ' . $p . "\n"; }
}
return $out;
}
// Global: todas las funciones IA del plugin (titulos, extractos, alt text, moderacion...).
add_filter( 'wpai_system_instruction', function ( $instruction ) {
return $instruction . d0a1_editorial_block();
}, 10, 1 );
// Afinar titulares: max chars configurable en editorial-style.php.
add_filter( 'wpai_title_generation_system_instruction', function ( $instruction ) {
$s = d0a1_editorial_style();
if ( $s && ! empty( $s['titulares_max_chars'] ) && $s['titulares_max_chars'] !== 80 ) {
$instruction = preg_replace( '/Be no more than \d+ characters/', 'Be no more than ' . (int) $s['titulares_max_chars'] . ' characters', $instruction );
}
return $instruction;
}, 10, 1 );