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: 16 additions & 97 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@ 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 @@ -29,7 +25,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 @@ -55,7 +51,6 @@ 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 @@ -68,78 +63,28 @@ jobs:
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
chmod 600 $HOME/.kube/config

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

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

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

echo "### Deployment - ${{ matrix.service.name }}" >> $GITHUB_STEP_SUMMARY
echo "### 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $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 "**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 "" >> $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 || true
kubectl get pods -n ${{ env.NAMESPACE }} -l app=${{ matrix.service.deployment }} >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY

notify-success:
Expand All @@ -150,34 +95,8 @@ jobs:
steps:
- name: Success Summary
run: |
echo "### All Services Deployed" >> $GITHUB_STEP_SUMMARY
echo "### ✅ Deployment Successful!" >> $GITHUB_STEP_SUMMARY
echo "" >> $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
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
22 changes: 1 addition & 21 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: PR Validation

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

Expand Down Expand Up @@ -67,13 +66,6 @@ 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 @@ -104,16 +96,4 @@ 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.13
3.14
21 changes: 4 additions & 17 deletions DocsManager/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
FROM python:3.12-slim
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim

WORKDIR /app

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

COPY pyproject.toml uv.lock* ./

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

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

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


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

@router.post(
"/messages",
response_model=AssistantMessageOut
)
def post_user_message(
payload: UserMessageIn,
async def post_user_message(
payload: RunAgentInput,
db: Session = Depends(get_db)
):
assistant_msg, session_id = create_user_message(
db=db,
message=payload.message,
session_id=payload.session_id
"""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)
Comment on lines +24 to +28
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring mentions returning "a stream of AG-UI events" but doesn't specify the format of these events or the expected media type. Consider adding more detail about the event format (e.g., whether they're JSON, SSE, newline-delimited) and the expected content-type to help API consumers understand how to consume the stream.

Suggested change
This endpoint accepts RunAgentInput and returns a stream of AG-UI events.
"""
return StreamingResponse(
process_agent_message(db, payload)
This endpoint accepts a :class:`RunAgentInput` payload and returns a
:class:`fastapi.responses.StreamingResponse` that streams AG-UI events.
The response body is a UTF-8 text stream where events are encoded as
Server-Sent Events (SSE) in standard format (e.g. ``data: ...`` lines
separated by a blank line). The HTTP ``Content-Type`` of the response is
``text/event-stream; charset=utf-8``, allowing AG-UI clients to consume
the stream as SSE.
"""
return StreamingResponse(
process_agent_message(db, payload),
media_type="text/event-stream",

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StreamingResponse requires a media_type parameter to be set for proper content-type headers. The AG-UI protocol likely expects a specific media type (such as "text/event-stream" for server-sent events). Add the media_type parameter to StreamingResponse to ensure proper streaming behavior.

Suggested change
process_agent_message(db, payload)
process_agent_message(db, payload),
media_type="text/event-stream",

Copilot uses AI. Check for mistakes.
)

return {
"session_id": session_id,
"message": assistant_msg.message
}
3 changes: 2 additions & 1 deletion 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, Field
from pydantic import field_validator, model_validator
from typing import Optional
from urllib.parse import quote_plus
import logging
Expand Down Expand Up @@ -29,6 +29,7 @@ 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: 0 additions & 12 deletions DocsManager/app/schemas/chatMessage.py

This file was deleted.

Loading