Compare commits
1 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
|
e882442f8b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.env
|
||||
.venv
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -18,7 +18,7 @@ from app.services.audio.transcription_adapters import transcribe_audio_with_prov
|
||||
# Inicializar el router de FastAPI para este módulo
|
||||
audio_router_transcription = APIRouter()
|
||||
|
||||
@audio_router_transcription.post("/transcripts/", response_model=StandardTranscriptionResult)
|
||||
@audio_router_transcription.post("/transcripts", response_model=StandardTranscriptionResult)
|
||||
async def transcribe_audio(audio_request: AudioRequestFile = Depends()) -> StandardTranscriptionResult:
|
||||
"""
|
||||
Endpoint para transcribir audio simple infiriendo el proveedor de IA
|
||||
|
||||
Binary file not shown.
37
app/api/v1/endpoints/document/rubricated_analysis.py
Normal file
37
app/api/v1/endpoints/document/rubricated_analysis.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
Gateway de IA de Qualidot - Módulo de Procesamiento de Documentos
|
||||
|
||||
Propósito:
|
||||
Este endpoint recibe un documento y lo analiza usando inteligencia artificial,
|
||||
extrayendo información relevante y estructurada.
|
||||
|
||||
Homologación:
|
||||
Sin importar qué proveedor de IA se utilice, el resultado siempre se entrega en el mismo formato estándar para Qualidot.
|
||||
"""
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException
|
||||
from app.schemas.document_standard import DocumentRequestFile, StandardDocumentAnalysisResult
|
||||
from app.services.document.evaluations_adapters import evaluate_document_with_provider
|
||||
|
||||
# Inicializar el router de FastAPI para este módulo
|
||||
document_router_analysis = APIRouter()
|
||||
|
||||
@document_router_analysis.post("/evaluations", response_model=StandardDocumentAnalysisResult)
|
||||
async def evaluate_document(document_request: DocumentRequestFile = Depends()) -> StandardDocumentAnalysisResult:
|
||||
"""
|
||||
Endpoint para analizar documentos usando inteligencia artificial
|
||||
|
||||
Args:
|
||||
document_request: Objeto DocumentRequestFile que contiene el archivo de documento,
|
||||
el proveedor, el modelo y la rúbrica de evaluación.
|
||||
|
||||
Returns:
|
||||
StandardDocumentAnalysisResult: Resultado del análisis de documentos en formato estándar de Qualidot
|
||||
"""
|
||||
try:
|
||||
# Analizar el documento usando el adaptador de análisis que infiere el proveedor
|
||||
analysis_result = await evaluate_document_with_provider(document_request)
|
||||
return analysis_result
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -22,13 +22,14 @@ from app.api.v1.endpoints.texto.resume import text_router_summary
|
||||
from app.api.v1.endpoints.texto.rubricated_analysis import text_router_analysis # Endpoint de análisis rubricado de texto
|
||||
from app.api.v1.endpoints.video.transcription import video_router_transcription # Endpoint de transcripción de video
|
||||
from app.api.v1.endpoints.image.rubricated_analysis import image_router_analysis # Endpoint de análisis rubricado de imágenes
|
||||
from app.api.v1.endpoints.document.rubricated_analysis import document_router_analysis # Endpoint de análisis rubricado de documentos
|
||||
|
||||
# Inicializar el router de FastAPI para los módulos de procesamiento
|
||||
api_router_audio = APIRouter()
|
||||
api_router_text = APIRouter()
|
||||
api_router_video = APIRouter()
|
||||
api_router_image = APIRouter()
|
||||
|
||||
api_router_document = APIRouter()
|
||||
|
||||
api_router_audio.include_router(
|
||||
audio_router_transcription,
|
||||
@@ -59,3 +60,9 @@ api_router_image.include_router(
|
||||
prefix="/image",
|
||||
tags=["Procesamiento de Imágenes"]
|
||||
)
|
||||
|
||||
api_router_document.include_router(
|
||||
document_router_analysis,
|
||||
prefix="/document",
|
||||
tags=["Procesamiento de Documentos"]
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
@@ -19,7 +19,7 @@ from app.services.text.resume_adapters import summarize_text_with_provider
|
||||
# Inicializar el router de FastAPI para este módulo
|
||||
text_router_summary = APIRouter()
|
||||
|
||||
@text_router_summary.post("/summaries/", response_model=StandardTextAnalysisResult)
|
||||
@text_router_summary.post("/summaries", response_model=StandardTextAnalysisResult)
|
||||
async def summarize_text(text_request: TextRequestFile = Depends()) -> StandardTextAnalysisResult:
|
||||
"""
|
||||
Endpoint para resumir texto simple infiriendo el proveedor de IA
|
||||
|
||||
@@ -19,7 +19,7 @@ from app.services.text.evaluations_adapters import evaluate_text_with_provider
|
||||
# Inicializar el router de FastAPI para este módulo
|
||||
text_router_analysis = APIRouter()
|
||||
|
||||
@text_router_analysis.post("/evaluations/", response_model=StandardTextAnalysisResult)
|
||||
@text_router_analysis.post("/evaluations", response_model=StandardTextAnalysisResult)
|
||||
async def evaluate_text(text_request: TextRequestFile = Depends()) -> StandardTextAnalysisResult:
|
||||
"""
|
||||
Endpoint para analizar texto usando una rúbrica de evaluación infiriendo el proveedor de IA
|
||||
|
||||
Binary file not shown.
@@ -16,7 +16,7 @@ from app.services.video.transcription_adapters import transcribe_video_with_prov
|
||||
# Inicializar el router de FastAPI para este módulo
|
||||
video_router_transcription = APIRouter()
|
||||
|
||||
@video_router_transcription.post("/transcripts/", response_model=StandardTranscriptionResult)
|
||||
@video_router_transcription.post("/transcripts", response_model=StandardTranscriptionResult)
|
||||
async def transcribe_video(video_request: VideoRequestFile = Depends()) -> StandardTranscriptionResult:
|
||||
"""
|
||||
Endpoint para transcribir video simple infiriendo el proveedor de IA
|
||||
|
||||
18
app/main.py
18
app/main.py
@@ -1,5 +1,5 @@
|
||||
from fastapi import FastAPI
|
||||
from app.api.v1.endpoints.router import api_router_audio, api_router_text, api_router_image, api_router_video
|
||||
from app.api.v1.endpoints.router import api_router_audio, api_router_text, api_router_image, api_router_video, api_router_document
|
||||
|
||||
app = FastAPI(
|
||||
title="Template de API de Procesamiento general",
|
||||
@@ -11,6 +11,8 @@ app.include_router(api_router_audio, prefix="/api/v1", tags=["Procesamiento de A
|
||||
app.include_router(api_router_text, prefix="/api/v1", tags=["Procesamiento de Texto"])
|
||||
app.include_router(api_router_image, prefix="/api/v1", tags=["Procesamiento de Imágenes"])
|
||||
app.include_router(api_router_video, prefix="/api/v1", tags=["Procesamiento de Video"])
|
||||
app.include_router(api_router_document, prefix="/api/v1", tags=["Procesamiento de Documentos"])
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def root():
|
||||
@@ -26,18 +28,22 @@ def root():
|
||||
"docs": "/docs",
|
||||
"endpoints": {
|
||||
"audio": {
|
||||
"transcripción de audio": "/api/v1/audio/transcripts/",
|
||||
"transcripción de audio": "/api/v1/audio/transcripts",
|
||||
},
|
||||
"texto": {
|
||||
"resumen de texto": "/api/v1/text/summaries/",
|
||||
"análisis rubricado": "/api/v1/text/evaluations/"
|
||||
"resumen de texto": "/api/v1/text/summaries",
|
||||
"análisis rubricado": "/api/v1/text/evaluations"
|
||||
},
|
||||
"imágenes": {
|
||||
"análisis rubricado": "/api/v1/image/evaluations/",
|
||||
"análisis rubricado": "/api/v1/image/evaluations",
|
||||
},
|
||||
"video": {
|
||||
"transcripción de video": "/api/v1/video/transcripts/"
|
||||
"transcripción de video": "/api/v1/video/transcripts"
|
||||
},
|
||||
"documentos": {
|
||||
"análisis rubricado": "/api/v1/document/evaluations"
|
||||
}
|
||||
|
||||
},
|
||||
"modelos_disponibles": {
|
||||
"audio": {
|
||||
|
||||
BIN
app/schemas/__pycache__/document_standard.cpython-312.pyc
Normal file
BIN
app/schemas/__pycache__/document_standard.cpython-312.pyc
Normal file
Binary file not shown.
60
app/schemas/document_standard.py
Normal file
60
app/schemas/document_standard.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""
|
||||
Esquema de resultado esperado de modelos de análisis de documentos.
|
||||
|
||||
Propósito:
|
||||
Define el formato estándar de los resultados devueltos por los modelos de análisis
|
||||
de documentos, independientemente del proveedor de IA utilizado.
|
||||
|
||||
Homologación:
|
||||
Garantiza que el resultado del análisis de documentos siempre se entregue en el mismo
|
||||
formato estándar para Qualidot.
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
|
||||
from fastapi import UploadFile
|
||||
from fastapi.params import File, Form
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Optional
|
||||
|
||||
class DocumentEvaluationCriteria(BaseModel):
|
||||
"""Modelo que representa un criterio de evaluación específico dentro de una rúbrica de análisis de imágenes."""
|
||||
name: str = Field(..., description="Nombre del criterio de evaluación")
|
||||
description: str = Field(None, description="Descripción detallada del criterio")
|
||||
score: float = Field(None, description="Puntuación asignada al criterio después del análisis")
|
||||
subcriteria: Optional[List["DocumentEvaluationCriteria"]] = Field(None, description="Lista de subcriterios de evaluación")
|
||||
|
||||
class DocumentEvaluationRubric(BaseModel):
|
||||
"""Modelo que representa una rúbrica de evaluación personalizada para análisis de documentos."""
|
||||
name: str = Field(..., description="Nombre de la rúbrica de evaluación")
|
||||
description: str = Field(None, description="Descripción detallada de la rúbrica")
|
||||
category: Optional[str] = Field(
|
||||
None,
|
||||
description="Contexto jerárquico de la categoría para especializar a la IA (ej. Audio > Education > English > B2)"
|
||||
)
|
||||
criteria: List[DocumentEvaluationCriteria] = Field(None, description="Lista de criterios de evaluación específicos")
|
||||
|
||||
@dataclass
|
||||
class DocumentRequestFile:
|
||||
"""Modelo de solicitud para análisis de documentos."""
|
||||
file: UploadFile = File(..., description="Archivo de documento a procesar (ej. pdf, docx)")
|
||||
provider: str = Form(..., description="Proveedor de IA a utilizar (ej. openai, google)")
|
||||
model: str = Form(..., description="Modelo de IA a utilizar (ej. vision-1)")
|
||||
rubric: UploadFile = File(..., description="Archivo JSON de evaluación")
|
||||
|
||||
class StandardDocumentAnalysisResult(BaseModel):
|
||||
"""Modelo que representa el resultado estándar de un análisis de documentos para Qualidot."""
|
||||
status: str = Field(..., description="Estado del análisis (ej. 'success', 'error')")
|
||||
original_filename: str = Field(..., description="Nombre original del archivo de documento procesado")
|
||||
provider_used: str = Field(..., description="Proveedor de IA utilizado para el análisis (ej. 'OpenAI')")
|
||||
model_used: str = Field(..., description="Modelo específico utilizado para el análisis (ej. 'vision-1')")
|
||||
score: float = Field(None, description="Puntuación general asignada al documento después del análisis")
|
||||
feedback: str = Field(None, description="Comentarios o retroalimentación generada por el modelo de IA sobre el documento")
|
||||
detailed_criteria: List[DocumentEvaluationCriteria] = Field(None, description="Lista de criterios de evaluación detallados con sus respectivas puntuaciones y descripciones")
|
||||
|
||||
|
||||
class StandardDocumentAnalysis(BaseModel):
|
||||
"""Modelo que representa el resultado estándar de un análisis de documentos para Qualidot."""
|
||||
score: float = Field(None, description="Puntuación general asignada al documento después del análisis")
|
||||
feedback: str = Field(None, description="Comentarios o retroalimentación generada por el modelo de IA sobre la imagen")
|
||||
detailed_criteria: List[DocumentEvaluationCriteria] = Field(None, description="Lista de criterios de evaluación detallados con sus respectivas puntuaciones y descripciones")
|
||||
|
||||
Binary file not shown.
BIN
app/services/document/__pycache__/prompt_builder.cpython-312.pyc
Normal file
BIN
app/services/document/__pycache__/prompt_builder.cpython-312.pyc
Normal file
Binary file not shown.
162
app/services/document/evaluations_adapters.py
Normal file
162
app/services/document/evaluations_adapters.py
Normal file
@@ -0,0 +1,162 @@
|
||||
"""
|
||||
Gateway de IA de Qualidot - Módulo de Adaptadores de Evaluación de Imágenes
|
||||
|
||||
Propósito:
|
||||
Este módulo contiene funciones de adaptadores que permiten evaluar imágenes usando diferentes proveedores de IA.
|
||||
Cada función de adaptador se encarga de interactuar con un proveedor específico (como OpenAI, AssemblyAI, Deepgram, etc.)
|
||||
y de convertir la respuesta del proveedor al formato estándar de evaluación de imágenes de Qualidot.
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
import mimetypes
|
||||
import mimetypes
|
||||
import tempfile
|
||||
import os
|
||||
from fastapi import HTTPException
|
||||
from matplotlib import image
|
||||
from openai import OpenAI, AsyncOpenAI
|
||||
import assemblyai as aai
|
||||
from pyparsing.common import Any
|
||||
from app.core.config import settings
|
||||
from app.schemas.document_standard import DocumentRequestFile, StandardDocumentAnalysisResult
|
||||
from app.core import config
|
||||
from app.utilities.document_utilities import json_to_rubric, encode_document_from_bytes
|
||||
from app.services.document.prompt_builder import build_document_evaluation_prompt
|
||||
from anthropic import Anthropic
|
||||
|
||||
from app.utilities.image_utilities import encode_image_from_bytes
|
||||
|
||||
# Función de adaptador principal que infiere el proveedor y llama al adaptador específico
|
||||
async def evaluate_document_with_provider(document_request: DocumentRequestFile) -> StandardDocumentAnalysisResult:
|
||||
"""
|
||||
Función de adaptador para evaluar documentos usando el proveedor de IA configurado.
|
||||
"""
|
||||
provider = document_request.provider.lower()
|
||||
|
||||
content = await document_request.rubric.read()
|
||||
rubric_dict = json.loads(content)
|
||||
rubric = json_to_rubric(rubric_dict)
|
||||
prompt = build_document_evaluation_prompt(rubric)
|
||||
|
||||
match provider:
|
||||
case "openai":
|
||||
return await evaluate_with_openai(document_request, prompt)
|
||||
case "claude":
|
||||
return await evaluate_with_claude(document_request, prompt)
|
||||
case "gemini":
|
||||
return await evaluate_with_gemini(document_request, prompt)
|
||||
case _:
|
||||
raise ValueError(f"Proveedor de IA no soportado: {document_request.provider}")
|
||||
|
||||
# Función de adaptador para evaluar documentos usando OpenAI
|
||||
async def evaluate_with_openai(document_request: DocumentRequestFile, prompt: str) -> StandardDocumentAnalysisResult:
|
||||
"""
|
||||
Función de adaptador para evaluar documentos usando OpenAI.
|
||||
"""
|
||||
|
||||
client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY)
|
||||
|
||||
document_bytes = await document_request.file.read()
|
||||
|
||||
base64_document = encode_document_from_bytes(document_bytes)
|
||||
media_type = document_request.file.content_type
|
||||
|
||||
try:
|
||||
response = await client.chat.completions.create(
|
||||
model=document_request.model,
|
||||
messages=[
|
||||
{"role": "user", "content": [
|
||||
{"type": "text", "text": prompt},
|
||||
{
|
||||
"type": "file",
|
||||
"file": {
|
||||
"file_data": f"data:{media_type};base64,{base64_document}",
|
||||
"filename": document_request.file.filename
|
||||
}
|
||||
}
|
||||
]}
|
||||
],
|
||||
response_format={"type": "json_object"}
|
||||
)
|
||||
|
||||
resultado = json.loads(response.choices[0].message.content)
|
||||
|
||||
return StandardDocumentAnalysisResult(
|
||||
status="success",
|
||||
original_filename=document_request.file.filename,
|
||||
provider_used="openai",
|
||||
model_used=document_request.model,
|
||||
**resultado
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Error evaluando el documento: {str(e)}"
|
||||
)
|
||||
|
||||
# Función de adaptador para evaluar documentos usando Claude
|
||||
async def evaluate_with_claude(document_request: DocumentRequestFile, prompt: str) -> StandardDocumentAnalysisResult:
|
||||
"""
|
||||
Función de adaptador para evaluar documentos usando Claude.
|
||||
"""
|
||||
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
|
||||
|
||||
document_bytes = await document_request.file.read()
|
||||
|
||||
base64_document = encode_document_from_bytes(document_bytes)
|
||||
media_type = document_request.file.content_type
|
||||
|
||||
if media_type not in ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"]:
|
||||
raise ValueError(f"Tipo de documento no soportado por Anthropic: {media_type}")
|
||||
|
||||
try:
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "document",
|
||||
"source": {
|
||||
"type": "base64",
|
||||
"media_type": media_type,
|
||||
"data": base64_document
|
||||
},
|
||||
},
|
||||
{"type": "text", "text": prompt}
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
response = client.messages.create(
|
||||
model=document_request.model,
|
||||
max_tokens=4096,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
json_string = response.content[0].text
|
||||
parsed_data = json.loads(json_string)
|
||||
|
||||
return StandardDocumentAnalysisResult(
|
||||
status="success",
|
||||
original_filename=document_request.file.filename,
|
||||
provider_used="Claude",
|
||||
model_used=document_request.model,
|
||||
**parsed_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Error evaluando el documento: {str(e)}"
|
||||
)
|
||||
|
||||
# Función de adaptador para evaluar documentos usando Gemini
|
||||
async def evaluate_with_gemini(document_request: DocumentRequestFile, prompt: str) -> StandardDocumentAnalysisResult:
|
||||
"""
|
||||
Función de adaptador para evaluar documentos usando Gemini.
|
||||
(Plantilla para futuras implementaciones)
|
||||
"""
|
||||
# Aquí iría la implementación específica para Gemini
|
||||
pass
|
||||
58
app/services/document/prompt_builder.py
Normal file
58
app/services/document/prompt_builder.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Módulo encargado de construir el prompt estandarizado para la evaluación de imágenes,
|
||||
inyectando la rúbrica, el contexto de especialidad y el esquema JSON esperado.
|
||||
Este módulo es crucial para asegurar que los evaluadores expertos reciban instrucciones
|
||||
claras y consistentes, y que sus respuestas se ajusten al formato requerido para su posterior
|
||||
procesamiento y análisis.
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
from app.schemas.document_standard import DocumentEvaluationRubric, StandardDocumentAnalysis
|
||||
|
||||
def build_document_evaluation_prompt(rubric: DocumentEvaluationRubric) -> str:
|
||||
"""
|
||||
Construye el prompt estandarizado inyectando la rúbrica,
|
||||
el contexto de especialidad y el esquema JSON esperado.
|
||||
"""
|
||||
|
||||
# Extraemos el JSON de la rúbrica de forma limpia (ignorando nulos)
|
||||
rubric_json = rubric.model_dump_json(exclude_none=True, indent=2)
|
||||
|
||||
# Extraemos el esquema dinámico de salida basado en Pydantic
|
||||
expected_output_schema = json.dumps(StandardDocumentAnalysis.model_json_schema(), indent=2)
|
||||
|
||||
# Obtenemos el path de especialización
|
||||
# Si por alguna razón viene vacío, le damos un rol genérico por defecto
|
||||
specialization_path = rubric.category if rubric.category else "General Document Analysis"
|
||||
|
||||
# Ensamblamos el prompt con f-strings
|
||||
prompt = f"""
|
||||
# ROLE
|
||||
You are a highly specialized Expert Evaluator with deep domain expertise in the following area: **{specialization_path}**. Your objective is to perform a highly accurate, objective, and domain-calibrated analysis of the provided input based STRICTLY on your specialization and the provided evaluation rubric.
|
||||
|
||||
# TASK
|
||||
I will provide you with an input and a JSON object representing an `EvaluationRubric`.
|
||||
Your task is to analyze the input and score it against every criterion and subcriterion defined in the rubric. You must justify your scores with objective feedback based on evidence.
|
||||
|
||||
# DOMAIN CONTEXT
|
||||
Specialization Path: {specialization_path}
|
||||
|
||||
# RUBRIC
|
||||
{rubric_json}
|
||||
|
||||
# EVALUATION RULES
|
||||
1. **Domain Calibration (CRITICAL):** Calibrate your expectations, strictness, and feedback entirely according to the **{specialization_path}** context. Do not evaluate using generalized standards; apply the specific standards expected at this exact level and category.
|
||||
2. **Strict Adherence:** Evaluate ONLY the criteria and subcriteria listed in the rubric. Do not invent new metrics.
|
||||
3. **Scoring:** Assign a numeric `score` to each criterion and subcriterion. The score must reflect how well the input meets the description of that specific metric.
|
||||
4. **Objective Feedback:** Generate constructive, evidence-grounded `feedback` for the overall evaluation. Mention specific elements or patterns observed in the input that justify the scores within the context of the specialization.
|
||||
5. **Subcriteria Handling:** If a criterion has `subcriteria`, evaluate and score each subcriterion individually. The parent criterion's `score` MUST be the exact mathematical **sum** of its subcriteria scores.
|
||||
6. **Final Score Calculation:** The overall final `score` of the evaluation MUST be the exact mathematical **sum** of all the main criteria scores.
|
||||
|
||||
# OUTPUT FORMAT
|
||||
You MUST return your response EXCLUSIVELY as a raw, valid JSON object that strictly adheres to the following JSON Schema definition. Do NOT include markdown blocks (```json), explanations, or any text outside the JSON object.
|
||||
|
||||
# EXPECTED JSON SCHEMA
|
||||
{expected_output_schema}
|
||||
"""
|
||||
return prompt
|
||||
Binary file not shown.
Binary file not shown.
@@ -24,6 +24,12 @@ from app.core import config
|
||||
from app.utilities.image_utilities import json_to_rubric, encode_image_from_bytes
|
||||
from app.services.image.prompt_builder import build_image_evaluation_prompt
|
||||
from anthropic import Anthropic
|
||||
import re
|
||||
|
||||
# Importaciones de Clarifai
|
||||
from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
|
||||
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
|
||||
from clarifai_grpc.grpc.api.status import status_code_pb2
|
||||
|
||||
# Función de adaptador principal que infiere el proveedor y llama al adaptador específico
|
||||
async def evaluate_image_with_provider(image_request: ImageRequestFile) -> StandardImageAnalysisResult:
|
||||
@@ -91,62 +97,77 @@ async def evaluate_with_openai(image_request: ImageRequestFile, prompt: str) ->
|
||||
detail=f"Error evaluando la imagen: {str(e)}"
|
||||
)
|
||||
|
||||
async def evaluate_with_clarifai(image_request: ImageRequestFile, rubric: ImageEvaluationRubric, prompt: str) -> StandardImageAnalysisResult:
|
||||
async def evaluate_with_clarifai(image_request: ImageRequestFile, prompt: str) -> StandardImageAnalysisResult:
|
||||
"""
|
||||
Función de adaptador para evaluar imágenes usando Clarifai con un modelo Multimodal.
|
||||
Función de adaptador para evaluar imágenes usando Clarifai.
|
||||
"""
|
||||
|
||||
|
||||
CLARIFAI_API_KEY = settings.CLARIFAI_API_KEY
|
||||
if not CLARIFAI_API_KEY:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="No se encontró CLARIFAI_API_KEY en las variables de entorno"
|
||||
)
|
||||
|
||||
USER_ID = "openai"
|
||||
APP_ID = "chat-completion"
|
||||
|
||||
try:
|
||||
# 1. Obtener el token de configuración (PAT)
|
||||
pat = settings.CLARIFAI_API_KEY
|
||||
if not pat:
|
||||
raise ValueError("La clave CLARIFAI_API_KEY no está configurada en el entorno.")
|
||||
|
||||
# 2. Obtener la URL del modelo enviada en la petición
|
||||
model_url = image_request.model
|
||||
|
||||
# Inicializar el modelo de Clarifai
|
||||
model = Model(url=model_url, pat=pat)
|
||||
|
||||
# 3. Leer los bytes de la imagen subida
|
||||
image_bytes = await image_request.file.read()
|
||||
|
||||
if not image_bytes:
|
||||
raise ValueError("El archivo de imagen recibido está vacío.")
|
||||
# 2. Preparamos y ejecutamos la llamada a Clarifai (gRPC)
|
||||
channel = ClarifaiChannel.get_grpc_channel()
|
||||
stub = service_pb2_grpc.V2Stub(channel)
|
||||
metadata = (('authorization', 'Key ' + CLARIFAI_API_KEY),)
|
||||
userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID, app_id=APP_ID)
|
||||
|
||||
# 4. Preparar el input multimodal para Clarifai combinando la imagen y el prompt de evaluación
|
||||
multimodal_input = Inputs.get_multimodal_input(
|
||||
input_id="image_evaluation",
|
||||
image_bytes=image_bytes,
|
||||
raw_text=prompt
|
||||
request = service_pb2.PostModelOutputsRequest(
|
||||
user_app_id=userDataObject,
|
||||
model_id=image_request.model,
|
||||
inputs=[
|
||||
resources_pb2.Input(
|
||||
data=resources_pb2.Data(
|
||||
image=resources_pb2.Image(base64=image_bytes),
|
||||
text=resources_pb2.Text(raw=prompt)
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
# 5. Llamar a la API de Clarifai para evaluar la imagen
|
||||
predict_response = model.predict([multimodal_input])
|
||||
response = stub.PostModelOutputs(request, metadata=metadata)
|
||||
|
||||
# Extraer el texto crudo de la respuesta del modelo
|
||||
raw_output = predict_response.outputs[0].data.text.raw
|
||||
if response.status.code != status_code_pb2.SUCCESS:
|
||||
raise Exception(f"Clarifai Error: {response.status.description}")
|
||||
|
||||
# 6. Limpiar la respuesta y convertirla a JSON
|
||||
# Los LLMs suelen devolver el JSON envuelto en bloques de markdown (```json ... ```)
|
||||
clean_json = raw_output.replace("```json", "").replace("```", "").strip()
|
||||
# 3. Extraemos la respuesta cruda
|
||||
raw_output = response.outputs[0].data.text.raw
|
||||
|
||||
# Convertir el string limpio a un diccionario de Python
|
||||
|
||||
|
||||
# 4. Limpiamos y parseamos el JSON devuelto
|
||||
json_match = re.search(r'\{.*\}', raw_output, re.DOTALL)
|
||||
|
||||
if json_match:
|
||||
clean_json = json_match.group(0)
|
||||
parsed_data = json.loads(clean_json)
|
||||
else:
|
||||
# Respaldo en caso de que el modelo devuelva texto plano en lugar de JSON
|
||||
parsed_data = {"raw_response": raw_output}
|
||||
|
||||
# 7. Retornar el resultado validado contra tu esquema estándar de Pydantic
|
||||
return StandardImageAnalysisResult(**parsed_data)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
# Error específico si el modelo alucinó texto extra y no devolvió un JSON válido
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"El modelo de Clarifai no devolvió un JSON válido. Error: {str(e)} | Respuesta cruda: {raw_output if 'raw_output' in locals() else 'N/A'}"
|
||||
# 5. Retornamos el modelo estandarizado
|
||||
return StandardImageAnalysisResult(
|
||||
status="success",
|
||||
original_filename=image_request.file.filename,
|
||||
provider_used="Clarifai",
|
||||
model_used=image_request.model,
|
||||
**parsed_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
# Captura cualquier otro error (problemas de red, token inválido, URL incorrecta, etc.)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Error interno al evaluar con Clarifai: {str(e)}"
|
||||
detail=f"Error evaluando la imagen con Clarifai: {str(e)}"
|
||||
)
|
||||
|
||||
async def evaluate_with_claude(image_request: ImageRequestFile, prompt: str) -> StandardImageAnalysisResult:
|
||||
|
||||
@@ -46,7 +46,8 @@ Specialization Path: {specialization_path}
|
||||
2. **Strict Adherence:** Evaluate ONLY the criteria and subcriteria listed in the rubric. Do not invent new metrics.
|
||||
3. **Scoring:** Assign a numeric `score` to each criterion and subcriterion. The score must reflect how well the input meets the description of that specific metric.
|
||||
4. **Objective Feedback:** Generate constructive, evidence-grounded `feedback` for the overall evaluation. Mention specific elements or patterns observed in the input that justify the scores within the context of the specialization.
|
||||
5. **Subcriteria Handling:** If a criterion has `subcriteria`, evaluate each subcriterion individually. The parent criterion's score should be a logical aggregate (e.g., average) of its subcriteria scores.
|
||||
5. **Subcriteria Handling:** If a criterion has `subcriteria`, evaluate and score each subcriterion individually. The parent criterion's `score` MUST be the exact mathematical **sum** of its subcriteria scores.
|
||||
6. **Final Score Calculation:** The overall final `score` of the evaluation MUST be the exact mathematical **sum** of all the main criteria scores.
|
||||
|
||||
# OUTPUT FORMAT
|
||||
You MUST return your response EXCLUSIVELY as a raw, valid JSON object that strictly adheres to the following JSON Schema definition. Do NOT include markdown blocks (```json), explanations, or any text outside the JSON object.
|
||||
|
||||
171
app/tests/rubricas/documentos/text.json
Normal file
171
app/tests/rubricas/documentos/text.json
Normal file
@@ -0,0 +1,171 @@
|
||||
{
|
||||
"rubric": {
|
||||
"id": "e7b9c1d2-4f5a-6b7c-8d9e-0a1b2c3d4e5f",
|
||||
"name": "Academic Essay Evaluation Rubric",
|
||||
"description": "Rúbrica detallada para evaluar la redacción de ensayos. Mide la calidad de la argumentación, la cohesión estructural y el dominio de la gramática y el vocabulario.",
|
||||
"user_id": "112b3fda-e380-4919-9f9d-ff941a3b1938",
|
||||
"status": true,
|
||||
"visibility": "private",
|
||||
"verified": true
|
||||
},
|
||||
"category": {
|
||||
"id": 14,
|
||||
"name": "Essay Evaluation",
|
||||
"status": true,
|
||||
"hierarchy": [
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Education",
|
||||
"level": 0,
|
||||
"parent_id": null
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Language Arts",
|
||||
"level": 1,
|
||||
"parent_id": 11
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "Academic Writing",
|
||||
"level": 2,
|
||||
"parent_id": 12
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "Essay Evaluation",
|
||||
"level": 3,
|
||||
"parent_id": 13
|
||||
}
|
||||
]
|
||||
},
|
||||
"criteria": [
|
||||
{
|
||||
"id": 100,
|
||||
"name": "Content & Argumentation",
|
||||
"description": "Evalúa la profundidad del contenido, la claridad de la tesis y la solidez de los argumentos presentados.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": null,
|
||||
"sub_criteria": [
|
||||
{
|
||||
"id": 101,
|
||||
"name": "Thesis Clarity",
|
||||
"description": "El ensayo presenta una tesis central clara, específica y debatible en la introducción.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 100
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"name": "Evidence & Support",
|
||||
"description": "Los argumentos están respaldados por evidencia sólida, ejemplos relevantes o citas bien integradas.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 100
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"name": "Critical Thinking",
|
||||
"description": "El texto demuestra un análisis profundo del tema, evitando generalidades superficiales o afirmaciones sin fundamento.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 100
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 200,
|
||||
"name": "Structure & Organization",
|
||||
"description": "Evalúa el flujo lógico del texto, la estructuración de párrafos y el uso de conectores.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": null,
|
||||
"sub_criteria": [
|
||||
{
|
||||
"id": 201,
|
||||
"name": "Logical Flow",
|
||||
"description": "Las ideas progresan de manera lógica. La introducción, el desarrollo y la conclusión están claramente definidos y equilibrados.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 200
|
||||
},
|
||||
{
|
||||
"id": 202,
|
||||
"name": "Paragraph Cohesion",
|
||||
"description": "Cada párrafo se centra en una idea principal única. Las transiciones entre párrafos y oraciones son fluidas y naturales.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 300,
|
||||
"name": "Language Mechanics & Style",
|
||||
"description": "Evalúa las convenciones ortográficas, gramaticales y la riqueza del vocabulario.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": null,
|
||||
"sub_criteria": [
|
||||
{
|
||||
"id": 301,
|
||||
"name": "Grammar & Syntax",
|
||||
"description": "Las oraciones están construidas correctamente. No hay errores de concordancia, tiempos verbales o sintaxis.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 300
|
||||
},
|
||||
{
|
||||
"id": 302,
|
||||
"name": "Spelling & Punctuation",
|
||||
"description": "Uso impecable de la ortografía y los signos de puntuación (comas, puntos, comillas, etc.).",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 300
|
||||
},
|
||||
{
|
||||
"id": 303,
|
||||
"name": "Vocabulary & Tone",
|
||||
"description": "El vocabulario es variado, preciso y adecuado para un contexto académico. El tono se mantiene formal y objetivo.",
|
||||
"type_criteria": "primary",
|
||||
"type_value": "numeric",
|
||||
"value": "10",
|
||||
"status": true,
|
||||
"criteria_id": 300
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "APA Style Guidelines for Academic Writing",
|
||||
"url": "https://apastyle.apa.org/style-grammar-guidelines",
|
||||
"reference_type": "official_doc",
|
||||
"is_primary": true,
|
||||
"description": "Guía oficial de estilo APA para redacción, gramática y estructuración de textos académicos.",
|
||||
"status": true
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
app/utilities/__pycache__/document_utilities.cpython-312.pyc
Normal file
BIN
app/utilities/__pycache__/document_utilities.cpython-312.pyc
Normal file
Binary file not shown.
51
app/utilities/document_utilities.py
Normal file
51
app/utilities/document_utilities.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from app.schemas.document_standard import DocumentEvaluationRubric, StandardDocumentAnalysis, DocumentEvaluationCriteria
|
||||
|
||||
def encode_document_from_bytes(document_bytes: bytes) -> str:
|
||||
"""
|
||||
Codifica un documento a base64 a partir de sus bytes.
|
||||
"""
|
||||
import base64
|
||||
return base64.b64encode(document_bytes).decode('utf-8')
|
||||
|
||||
def json_to_rubric(json_data: dict) -> DocumentEvaluationRubric:
|
||||
"""Convierte un diccionario JSON en un objeto DocumentEvaluationRubric mapeando los campos correctos."""
|
||||
|
||||
def parse_criteria(criteria_list: list) -> list:
|
||||
parsed_criteria = []
|
||||
for item in criteria_list:
|
||||
# Convertir el valor a float si existe
|
||||
score_value = float(item["value"]) if item.get("value") is not None else None
|
||||
description = str(item["description"]) if item.get("description") is not None else None
|
||||
|
||||
# Procesar subcriterios recursivamente si existen
|
||||
sub_raw = item.get("sub_criteria")
|
||||
sub_parsed = parse_criteria(sub_raw) if sub_raw else None
|
||||
|
||||
parsed_criteria.append(DocumentEvaluationCriteria(
|
||||
name=item["name"],
|
||||
description=description,
|
||||
score=score_value,
|
||||
subcriteria=sub_parsed
|
||||
))
|
||||
return parsed_criteria
|
||||
|
||||
# Jerarquía de la categoría
|
||||
category_info = json_data.get("category", {})
|
||||
hierarchy_list = category_info.get("hierarchy", [])
|
||||
|
||||
# Ordenamos por nivel para asegurar la lógica: Nivel 0 -> Nivel 1 -> Nivel 2
|
||||
sorted_hierarchy = sorted(hierarchy_list, key=lambda x: x.get("level", 0))
|
||||
|
||||
# Unimos los nombres con ' > '
|
||||
specialization_path = " > ".join([item["name"] for item in sorted_hierarchy]) if sorted_hierarchy else None
|
||||
|
||||
# Extraer la información principal de la rúbrica
|
||||
rubric_info = json_data.get("rubric", {})
|
||||
|
||||
return DocumentEvaluationRubric(
|
||||
name=rubric_info.get("name", "Sin nombre"),
|
||||
description=str(rubric_info["description"]) if rubric_info.get("description") is not None else None,
|
||||
category=specialization_path,
|
||||
criteria=parse_criteria(json_data.get("criteria", []))
|
||||
)
|
||||
|
||||
168
requirements.txt
168
requirements.txt
@@ -1,29 +1,143 @@
|
||||
# Dependencias principales
|
||||
annotated-doc==0.0.4
|
||||
annotated-types==0.7.0
|
||||
anyio==4.11.0
|
||||
fastapi[standard]==0.121.3
|
||||
idna==3.11
|
||||
pydantic==2.12.4
|
||||
pydantic_core==2.41.5
|
||||
sniffio==1.3.1
|
||||
starlette==0.50.0
|
||||
typing-inspection==0.4.2
|
||||
typing_extensions==4.15.0
|
||||
uvicorn[standard]
|
||||
python-multipart
|
||||
python-dotenv
|
||||
# ==========================================
|
||||
# Web & API Framework (FastAPI)
|
||||
# ==========================================
|
||||
fastapi==0.121.3 # Framework principal para crear la API
|
||||
fastapi-cli==0.0.24 # Interfaz de línea de comandos para FastAPI
|
||||
fastapi-cloud-cli==0.15.0 # Herramientas de despliegue para FastAPI
|
||||
starlette==0.50.0 # Toolkit ASGI base de FastAPI
|
||||
uvicorn==0.41.0 # Servidor ASGI para ejecutar la aplicación
|
||||
uvloop==0.22.1 # Ciclo de eventos rápido para Uvicorn
|
||||
anyio==4.11.0 # Soporte asíncrono para concurrencia
|
||||
sniffio==1.3.1 # Detección de la librería asíncrona en uso
|
||||
websockets==16.0 # Soporte para conexiones WebSockets
|
||||
watchfiles==1.1.1 # Recarga automática del servidor al detectar cambios
|
||||
python-multipart==0.0.22 # Manejo de datos de formularios y subida de archivos
|
||||
|
||||
# Procesamiento de audio
|
||||
noisereduce
|
||||
librosa
|
||||
soundfile
|
||||
praat-parselmouth
|
||||
numpy
|
||||
# ==========================================
|
||||
# Validación de Datos & Tipado
|
||||
# ==========================================
|
||||
pydantic==2.12.4 # Validación de datos y gestión de esquemas
|
||||
pydantic_core==2.41.5 # Núcleo en Rust para Pydantic (rendimiento)
|
||||
pydantic-extra-types==2.11.0 # Tipos adicionales para Pydantic
|
||||
pydantic-settings==2.13.1 # Manejo avanzado de variables de entorno
|
||||
annotated-types==0.7.0 # Metadatos para el tipado de variables
|
||||
typing_extensions==4.15.0 # Funciones de tipado para versiones antiguas de Python
|
||||
typing-inspection==0.4.2 # Inspección de tipos en tiempo de ejecución
|
||||
email-validator==2.3.0 # Validación de correos electrónicos
|
||||
|
||||
# Modelos de IA e integraciones
|
||||
openai
|
||||
langchain
|
||||
langchain-openai
|
||||
assemblyai
|
||||
anthropic
|
||||
# ==========================================
|
||||
# Modelos de IA, LLMs & Agentes (LangChain)
|
||||
# ==========================================
|
||||
openai==2.28.0 # Cliente oficial para la API de OpenAI
|
||||
langchain==1.2.12 # Framework para aplicaciones con LLMs
|
||||
langchain-core==1.2.19 # Componentes y abstracciones base de LangChain
|
||||
langchain-openai==1.1.11 # Integración específica de OpenAI para LangChain
|
||||
langgraph==1.1.2 # Creación de agentes y flujos cíclicos con LLMs
|
||||
langgraph-checkpoint==4.0.1 # Gestión de estados y memoria para LangGraph
|
||||
langgraph-prebuilt==1.0.8 # Componentes preconstruidos para LangGraph
|
||||
langgraph-sdk==0.3.11 # SDK oficial de LangGraph
|
||||
langsmith==0.7.17 # Monitoreo, trazas y depuración para LangChain
|
||||
tiktoken==0.12.0 # Tokenizador rápido usado por OpenAI
|
||||
|
||||
# ==========================================
|
||||
# Procesamiento de Audio & Voz (Speech-to-Text)
|
||||
# ==========================================
|
||||
assemblyai==0.56.0 # SDK de AssemblyAI para transcripción de audio
|
||||
deepgram-sdk==6.0.1 # SDK de Deepgram para transcripción de audio
|
||||
librosa==0.11.0 # Análisis de música y señales de audio
|
||||
noisereduce==3.0.3 # Algoritmos para reducción de ruido en audio
|
||||
praat-parselmouth==0.4.7 # Interfaz de Python para Praat (análisis fonético)
|
||||
soundfile==0.13.1 # Lectura y escritura de archivos de audio
|
||||
audioread==3.1.0 # Decodificación de audio multiplataforma
|
||||
soxr==1.0.0 # Conversión de frecuencia de muestreo (resampling) de alta calidad
|
||||
|
||||
# ==========================================
|
||||
# Ciencia de Datos, Matemáticas & Machine Learning
|
||||
# ==========================================
|
||||
numpy==2.4.3 # Computación numérica y manejo de arreglos (matrices)
|
||||
scipy==1.17.1 # Funciones matemáticas, científicas y de ingeniería
|
||||
scikit-learn==1.8.0 # Herramientas de Machine Learning y análisis de datos
|
||||
numba==0.64.0 # Compilador JIT (Just-In-Time) para acelerar código matemático
|
||||
llvmlite==0.46.0 # Motor subyacente para compilar con Numba
|
||||
joblib==1.5.3 # Procesamiento en paralelo y caché (usado por scikit-learn)
|
||||
threadpoolctl==3.6.0 # Control de hilos en librerías nativas (C/C++)
|
||||
|
||||
# ==========================================
|
||||
# Redes & Clientes HTTP
|
||||
# ==========================================
|
||||
requests==2.32.5 # Cliente HTTP síncrono estándar
|
||||
requests-toolbelt==1.0.0 # Utilidades adicionales para la librería requests
|
||||
httpx==0.28.1 # Cliente HTTP asíncrono (alternativa moderna a requests)
|
||||
httpcore==1.0.9 # Motor subyacente de red para HTTPX
|
||||
httptools==0.7.1 # Analizador (parser) de peticiones HTTP ultrarrápido
|
||||
h11==0.16.0 # Implementación pura de HTTP/1.1
|
||||
urllib3==2.6.3 # Cliente HTTP base con gestión de conexiones y reintentos
|
||||
certifi==2026.2.25 # Colección de certificados SSL/TLS raíz
|
||||
idna==3.11 # Soporte para nombres de dominio internacionalizados
|
||||
dnspython==2.8.0 # Herramientas para consultas y manipulación de DNS
|
||||
|
||||
# ==========================================
|
||||
# Gráficos & Visualización
|
||||
# ==========================================
|
||||
matplotlib==3.10.8 # Creación de gráficas y visualizaciones de datos
|
||||
contourpy==1.3.3 # Cálculo de contornos 2D (dependencia de matplotlib)
|
||||
cycler==0.12.1 # Creación de iteradores complejos (dependencia de matplotlib)
|
||||
fonttools==4.62.1 # Manipulación de fuentes tipográficas
|
||||
kiwisolver==1.5.0 # Solucionador matemático rápido (dependencia de matplotlib)
|
||||
pillow==12.1.1 # Procesamiento y manipulación de imágenes (PIL)
|
||||
pyparsing==3.3.2 # Herramienta para crear analizadores de texto sintácticos
|
||||
|
||||
# ==========================================
|
||||
# CLI (Terminal) & Utilidades de Salida
|
||||
# ==========================================
|
||||
click==8.3.1 # Creación rápida de interfaces de línea de comandos (CLI)
|
||||
typer==0.24.1 # Creación de CLIs basado en Pydantic y tipado
|
||||
rich==14.3.3 # Texto enriquecido, tablas y colores en la terminal
|
||||
rich-toolkit==0.19.7 # Componentes adicionales para Rich
|
||||
tqdm==4.67.3 # Barras de progreso visuales en consola
|
||||
shellingham==1.5.4 # Herramienta para detectar qué shell se está utilizando
|
||||
|
||||
# ==========================================
|
||||
# Serialización, Parsing & Utilidades Generales
|
||||
# ==========================================
|
||||
python-dotenv==1.2.2 # Carga de variables de entorno desde archivos .env
|
||||
orjson==3.11.7 # Analizador (parser) de JSON ultrarrápido
|
||||
jiter==0.13.0 # Parser de JSON eficiente (usado internamente por Pydantic)
|
||||
ormsgpack==1.12.2 # Serialización de datos en formato MessagePack (rápida)
|
||||
msgpack==1.1.2 # Serialización de datos en formato MessagePack (estándar)
|
||||
PyYAML==6.0.3 # Procesamiento de archivos YAML
|
||||
jsonpatch==1.33 # Aplicación de parches a documentos JSON
|
||||
jsonpointer==3.0.0 # Identificación de nodos dentro de un JSON
|
||||
python-dateutil==2.9.0.post0 # Extensiones robustas para el manejo de fechas (datetime)
|
||||
regex==2026.2.28 # Motor de expresiones regulares alternativo y más potente
|
||||
uuid_utils==0.14.1 # Utilidades para la generación rápida de UUIDs
|
||||
charset-normalizer==3.4.5 # Detección automática de codificación de texto
|
||||
six==1.17.0 # Librería de compatibilidad entre Python 2 y 3
|
||||
tenacity==9.1.4 # Reintentos automáticos para código propenso a fallos
|
||||
decorator==5.2.1 # Simplificación en la creación de decoradores
|
||||
xxhash==3.6.0 # Algoritmo de hash no criptográfico extremadamente rápido
|
||||
zstandard==0.25.0 # Compresión de datos rápida (algoritmo zstd)
|
||||
packaging==26.0 # Manejo y parseo de versiones de paquetes de Python
|
||||
platformdirs==4.9.4 # Identificación de rutas de directorios estándar del SO
|
||||
pooch==1.9.0 # Descarga y almacenamiento en caché de archivos de datos
|
||||
lazy-loader==0.5 # Carga perezosa (lazy) de módulos pesados
|
||||
cffi==2.0.0 # Interfaz para llamar código en C desde Python (FFI)
|
||||
pycparser==3.0 # Analizador sintáctico de lenguaje C en Python
|
||||
rignore==0.7.6 # Herramienta para analizar archivos ignorados (ej. .gitignore)
|
||||
annotated-doc==0.0.4 # Utilidades para extraer documentación de tipos anotados
|
||||
fastar==0.8.0 # Utilidad secundaria (generalmente vinculada al framework web)
|
||||
|
||||
# ==========================================
|
||||
# Plantillas & Procesamiento de Markdown
|
||||
# ==========================================
|
||||
Jinja2==3.1.6 # Motor de plantillas (usado comúnmente para renderizar HTML)
|
||||
MarkupSafe==3.0.3 # Escapado seguro de strings para evitar inyecciones en HTML
|
||||
markdown-it-py==4.0.0 # Analizador y renderizador de Markdown extensible
|
||||
mdurl==0.1.2 # Utilidad para parsear URLs dentro de Markdown
|
||||
Pygments==2.19.2 # Resaltador de sintaxis genérico para código fuente
|
||||
|
||||
# ==========================================
|
||||
# Monitoreo & Diagnóstico del Sistema
|
||||
# ==========================================
|
||||
sentry-sdk==2.54.0 # Integración con Sentry para monitoreo y rastreo de errores
|
||||
distro==1.9.0 # Extracción de información específica del sistema operativo Linux
|
||||
Reference in New Issue
Block a user