Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 97 additions & 16 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ on:
env:
REGISTRY: crretoxmas2024.azurecr.io
NAMESPACE: reto-xmas-2025-goland-ia-backend
ROLLOUT_TIMEOUT: 60s
READY_CHECK_RETRIES: 20
READY_CHECK_SLEEP: 15

jobs:
build-and-deploy:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
Expand All @@ -25,7 +29,7 @@ jobs:
path: ./RAGManager
image: reto-xmas-2025-goland-ia-backend-rag-manager
deployment: rag-manager

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -51,6 +55,7 @@ jobs:
${{ env.REGISTRY }}/${{ matrix.service.image }}:${{ github.sha }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ matrix.service.image }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ matrix.service.image }}:buildcache,mode=max
provenance: false

- name: Set up kubectl
uses: azure/setup-kubectl@v3
Expand All @@ -63,28 +68,78 @@ jobs:
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
chmod 600 $HOME/.kube/config

- name: Restart deployment
- name: Update deployment image
run: |
kubectl rollout restart deployment/${{ matrix.service.deployment }} -n ${{ env.NAMESPACE }}
kubectl rollout status deployment/${{ matrix.service.deployment }} -n ${{ env.NAMESPACE }} --timeout=5m
kubectl set image deployment/${{ matrix.service.deployment }} \
api=${{ env.REGISTRY }}/${{ matrix.service.image }}:${{ github.sha }} \
-n ${{ env.NAMESPACE }}

- name: Verify deployment
- name: Wait for deployment to be ready (robust)
run: |
echo "✅ Deployment successful for ${{ matrix.service.name }}"
kubectl get pods -n ${{ env.NAMESPACE }} -l app=${{ matrix.service.deployment }}
set -e

echo "Checking rollout status (non-blocking)..."
kubectl rollout status deployment/${{ matrix.service.deployment }} \
-n ${{ env.NAMESPACE }} \
--timeout=${{ env.ROLLOUT_TIMEOUT }} || true

echo "Waiting for available replicas..."

for i in $(seq 1 $READY_CHECK_RETRIES); do
DESIRED=$(kubectl get deployment/${{ matrix.service.deployment }} -n ${{ env.NAMESPACE }} -o jsonpath='{.spec.replicas}')
AVAILABLE=$(kubectl get deployment/${{ matrix.service.deployment }} -n ${{ env.NAMESPACE }} -o jsonpath='{.status.availableReplicas}')

echo "Attempt $i: $AVAILABLE / $DESIRED replicas available"

if [ "$AVAILABLE" = "$DESIRED" ]; then
echo "Deployment is ready"
exit 0
fi

sleep $READY_CHECK_SLEEP
done

echo "Deployment did not become ready in time"
kubectl describe deployment/${{ matrix.service.deployment }} -n ${{ env.NAMESPACE }}
exit 1

- name: Verify pods
if: success()
run: |
kubectl get pods -n ${{ env.NAMESPACE }} -l app=${{ matrix.service.deployment }} -o wide

- name: Get logs on failure
if: failure()
run: |
echo "=== Deployment ==="
kubectl describe deployment/${{ matrix.service.deployment }} -n ${{ env.NAMESPACE }}

echo "=== Pods ==="
kubectl get pods -n ${{ env.NAMESPACE }} -l app=${{ matrix.service.deployment }} -o wide

echo "=== Pod Logs ==="
kubectl logs -n ${{ env.NAMESPACE }} \
-l app=${{ matrix.service.deployment }} \
--tail=100 \
--all-containers=true \
--prefix=true || true

- name: Deployment Summary
if: always()
run: |
echo "### 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY
STATUS="${{ job.status }}"

echo "### Deployment - ${{ matrix.service.name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Service:** ${{ matrix.service.name }}" >> $GITHUB_STEP_SUMMARY
echo "**Image:** ${{ env.REGISTRY }}/${{ matrix.service.image }}:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Image | \`${{ env.REGISTRY }}/${{ matrix.service.image }}:${{ github.sha }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Status | $STATUS |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

echo "#### Pods:" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
kubectl get pods -n ${{ env.NAMESPACE }} -l app=${{ matrix.service.deployment }} >> $GITHUB_STEP_SUMMARY
kubectl get pods -n ${{ env.NAMESPACE }} -l app=${{ matrix.service.deployment }} >> $GITHUB_STEP_SUMMARY || true
echo '```' >> $GITHUB_STEP_SUMMARY

notify-success:
Expand All @@ -95,8 +150,34 @@ jobs:
steps:
- name: Success Summary
run: |
echo "### ✅ Deployment Successful!" >> $GITHUB_STEP_SUMMARY
echo "### All Services Deployed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All services deployed successfully:" >> $GITHUB_STEP_SUMMARY
echo "- 🌐 DocsManager: https://goland-ia-backend-docs-manager.reto-ucu.net" >> $GITHUB_STEP_SUMMARY
echo "- 🌐 RAGManager: https://goland-ia-backend-rag-manager.reto-ucu.net" >> $GITHUB_STEP_SUMMARY
echo "**Live URLs:**" >> $GITHUB_STEP_SUMMARY
echo "- [DocsManager](https://goland-ia-backend-docs-manager.reto-ucu.net/docs)" >> $GITHUB_STEP_SUMMARY
echo "- [RAGManager](https://goland-ia-backend-rag-manager.reto-ucu.net/docs)" >> $GITHUB_STEP_SUMMARY

- name: Notificar a Discord (deploy exitoso)
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"✅ **Deploy exitoso**\\n\\n**Servicio:** \\\\`${{ matrix.service.name }}\\\\`\\n**Imagen:** \\\\`${{ env.REGISTRY }}/${{ matrix.service.image }}:${{ github.sha }}\\\\`\\n**Namespace:** \\\\`${{ env.NAMESPACE }}\\\\`\\n**Autor:** ${{ github.actor }}\\n**Commit:** [${{ github.sha }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})\\n\\n[Ver ejecución en GitHub Actions](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\\n\\n:rocket: ¡Todo salió bien!\"}" \
https://discord.com/api/webhooks/1449147972330852463/sMs3QYOvQ6oiaPHR4PlBluwNrhWSsVmfm3_Miz6UAFrPxj1SsbqNnHXc5eK9h8ZSaumk

notify-failure:
name: Deployment Failed
runs-on: ubuntu-latest
needs: [build-and-deploy]
if: failure()
steps:
- name: Failure Summary
run: |
echo "### Deployment Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Please check deployment logs above." >> $GITHUB_STEP_SUMMARY

- name: Notificar a Discord (deploy fallido)
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"❌ **Falló el deploy**\\n\\n**Servicio:** \\\\`${{ matrix.service.name }}\\\\`\\n**Imagen:** \\\\`${{ env.REGISTRY }}/${{ matrix.service.image }}:${{ github.sha }}\\\\`\\n**Namespace:** \\\\`${{ env.NAMESPACE }}\\\\`\\n**Autor:** ${{ github.actor }}\\n**Commit:** [${{ github.sha }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})\\n\\n[Ver ejecución en GitHub Actions](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\\n\\n:warning: Revisa los logs para más detalles.\"}" \
https://discord.com/api/webhooks/1449147972330852463/sMs3QYOvQ6oiaPHR4PlBluwNrhWSsVmfm3_Miz6UAFrPxj1SsbqNnHXc5eK9h8ZSaumk
22 changes: 21 additions & 1 deletion .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: PR Validation

on:
pull_request:
types: [opened]
branches:
- main

Expand Down Expand Up @@ -66,6 +67,13 @@ jobs:
severity: 'CRITICAL,HIGH'
exit-code: '0'

- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@v2
with:
host: https://sonarqube.reto-ucu.net/
login: ${{ secrets.SONAR_TOKEN }}
projectKey: reto-xmas-2025-goland-ia-backend

pr-summary:
name: PR Summary
runs-on: ubuntu-latest
Expand Down Expand Up @@ -96,4 +104,16 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});
});

discord-pr-notify:
name: Notificar PR en Discord
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Notificar a Discord (nuevo PR)
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"🔔 Nuevo Pull Request: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) por ${{ github.event.pull_request.user.login }}\"}" \
https://discord.com/api/webhooks/1449147972330852463/sMs3QYOvQ6oiaPHR4PlBluwNrhWSsVmfm3_Miz6UAFrPxj1SsbqNnHXc5eK9h8ZSaumk
2 changes: 1 addition & 1 deletion DocsManager/.python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.14
3.13
21 changes: 17 additions & 4 deletions DocsManager/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
FROM python:3.12-slim

WORKDIR /app

COPY pyproject.toml uv.lock* ./
RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/*

RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
mv /root/.local/bin/uv /usr/local/bin/uv && \
mv /root/.local/bin/uvx /usr/local/bin/uvx


RUN uv sync --frozen --no-cache || uv sync --no-cache

COPY . .

RUN uv sync --no-dev --no-cache \
&& uv pip list \
&& uv pip show fastapi uvicorn

EXPOSE 8000

CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1

CMD ["uv", "run", "--no-sync", "python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
25 changes: 14 additions & 11 deletions DocsManager/app/api/routes/chatMessage.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from fastapi import APIRouter, Depends
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session

from app.core.db_connection import get_db
from ag_ui.core import RunAgentInput
from app.services.chatMessage import process_agent_message
from app.schemas.chatMessage import UserMessageIn, AssistantMessageOut
from app.services.chatMessage import create_user_message


router = APIRouter(
Expand All @@ -15,15 +14,19 @@

@router.post(
"/messages",
response_model=AssistantMessageOut
)
async def post_user_message(
payload: RunAgentInput,
def post_user_message(
payload: UserMessageIn,
db: Session = Depends(get_db)
):
"""Handle chat messages using the AG-UI protocol.

This endpoint accepts RunAgentInput and returns a stream of AG-UI events.
"""
return StreamingResponse(
process_agent_message(db, payload)
assistant_msg, session_id = create_user_message(
db=db,
message=payload.message,
session_id=payload.session_id
)

return {
"session_id": session_id,
"message": assistant_msg.message
}
3 changes: 1 addition & 2 deletions DocsManager/app/core/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import field_validator, model_validator
from pydantic import field_validator, model_validator, Field
from typing import Optional
from urllib.parse import quote_plus
import logging
Expand Down Expand Up @@ -29,7 +29,6 @@ class Settings(BaseSettings):
minio_access_key: str
minio_secret_key: str
minio_bucket: str = "documents"
minio_folder: str = "rag-docs"
minio_use_ssl: bool = True

# Database Configuration (for SQLAlchemy)
Expand Down
12 changes: 12 additions & 0 deletions DocsManager/app/schemas/chatMessage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pydantic import BaseModel
from uuid import UUID
from typing import Optional

class UserMessageIn(BaseModel):
session_id: Optional[UUID] = None
message: str


class AssistantMessageOut(BaseModel):
session_id: UUID
message: str
Loading
Loading