✅ IMPLEMENTADO - Soporte para Anthropic Claude y Corrección de Configuración
🔧 Backend v1.2.0 | Frontend v1.2.5 He implementado completamente el soporte para Anthropic Claude y corregido el problema de visualización en el módulo de configuración: 🎯 Cambios Implementados: 1. Backend - Soporte para Anthropic Claude Nuevos modelos agregados: Claude Sonnet 4.5 (Recomendado) - Equilibrio perfecto Claude Opus 4.5 - Máxima capacidad Claude Haiku 4.5 - Ultra rápido y económico Integración completa: Chat Assistant con Anthropic Generación de resúmenes PDF con Anthropic Manejo correcto de formato de mensajes (system separado) Configuración por defecto: claude-sonnet-4.5
This commit is contained in:
@@ -276,7 +276,7 @@ def extract_pdf_text_smart(pdf_content: bytes, max_chars: int = None) -> dict:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BACKEND_VERSION = "1.1.2"
|
BACKEND_VERSION = "1.2.0"
|
||||||
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
|
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
|
||||||
|
|
||||||
# S3/MinIO configuration
|
# S3/MinIO configuration
|
||||||
@@ -1512,12 +1512,12 @@ REGLAS:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Usar OpenAI o Gemini según configuración
|
# Usar OpenAI, Anthropic o Gemini según configuración
|
||||||
if config.provider == "openai" and config.openai_api_key:
|
if config.provider == "openai":
|
||||||
client = openai.OpenAI(api_key=config.openai_api_key)
|
client = openai.OpenAI(api_key=config.api_key)
|
||||||
response = await asyncio.to_thread(
|
response = await asyncio.to_thread(
|
||||||
client.chat.completions.create,
|
client.chat.completions.create,
|
||||||
model=config.openai_model or "gpt-4o",
|
model=config.model_name or "gpt-4o",
|
||||||
messages=[{"role": "user", "content": summary_prompt}],
|
messages=[{"role": "user", "content": summary_prompt}],
|
||||||
temperature=0.3,
|
temperature=0.3,
|
||||||
max_tokens=800,
|
max_tokens=800,
|
||||||
@@ -1525,10 +1525,22 @@ REGLAS:
|
|||||||
)
|
)
|
||||||
summary_json = response.choices[0].message.content
|
summary_json = response.choices[0].message.content
|
||||||
|
|
||||||
elif config.provider == "gemini" and config.gemini_api_key:
|
elif config.provider == "anthropic":
|
||||||
genai.configure(api_key=config.gemini_api_key)
|
import anthropic as anthropic_lib
|
||||||
|
client = anthropic_lib.Anthropic(api_key=config.api_key)
|
||||||
|
response = await asyncio.to_thread(
|
||||||
|
client.messages.create,
|
||||||
|
model=config.model_name or "claude-sonnet-4-5",
|
||||||
|
max_tokens=800,
|
||||||
|
temperature=0.3,
|
||||||
|
messages=[{"role": "user", "content": summary_prompt + "\n\nRespuesta en formato JSON:"}]
|
||||||
|
)
|
||||||
|
summary_json = response.content[0].text
|
||||||
|
|
||||||
|
elif config.provider == "gemini":
|
||||||
|
genai.configure(api_key=config.api_key)
|
||||||
model = genai.GenerativeModel(
|
model = genai.GenerativeModel(
|
||||||
model_name=config.gemini_model or "gemini-2.0-flash-exp",
|
model_name=config.model_name or "gemini-2.5-pro",
|
||||||
generation_config={
|
generation_config={
|
||||||
"temperature": 0.3,
|
"temperature": 0.3,
|
||||||
"max_output_tokens": 800,
|
"max_output_tokens": 800,
|
||||||
@@ -2726,6 +2738,25 @@ def get_available_ai_models(current_user: models.User = Depends(get_current_user
|
|||||||
"name": "Gemini 1.5 Flash Latest",
|
"name": "Gemini 1.5 Flash Latest",
|
||||||
"provider": "gemini",
|
"provider": "gemini",
|
||||||
"description": "Modelo 1.5 rápido para análisis básicos"
|
"description": "Modelo 1.5 rápido para análisis básicos"
|
||||||
|
},
|
||||||
|
# Anthropic Claude Models
|
||||||
|
{
|
||||||
|
"id": "claude-sonnet-4-5",
|
||||||
|
"name": "Claude Sonnet 4.5 (Recomendado)",
|
||||||
|
"provider": "anthropic",
|
||||||
|
"description": "Equilibrio perfecto entre velocidad e inteligencia, ideal para diagnósticos automotrices"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "claude-opus-4-5",
|
||||||
|
"name": "Claude Opus 4.5",
|
||||||
|
"provider": "anthropic",
|
||||||
|
"description": "Máxima capacidad para análisis complejos y razonamiento profundo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "claude-haiku-4-5",
|
||||||
|
"name": "Claude Haiku 4.5",
|
||||||
|
"provider": "anthropic",
|
||||||
|
"description": "Ultra rápido y económico, perfecto para análisis en tiempo real"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2771,6 +2802,8 @@ def create_ai_configuration(
|
|||||||
model_name = "gpt-4o"
|
model_name = "gpt-4o"
|
||||||
elif config.provider == "gemini":
|
elif config.provider == "gemini":
|
||||||
model_name = "gemini-2.5-pro"
|
model_name = "gemini-2.5-pro"
|
||||||
|
elif config.provider == "anthropic":
|
||||||
|
model_name = "claude-sonnet-4-5"
|
||||||
else:
|
else:
|
||||||
model_name = "default"
|
model_name = "default"
|
||||||
|
|
||||||
@@ -3510,6 +3543,28 @@ Longitud: {response_length}
|
|||||||
ai_response = response.choices[0].message.content
|
ai_response = response.choices[0].message.content
|
||||||
confidence = 0.85 # OpenAI no devuelve confidence directo
|
confidence = 0.85 # OpenAI no devuelve confidence directo
|
||||||
|
|
||||||
|
elif ai_config.provider == 'anthropic':
|
||||||
|
import anthropic
|
||||||
|
|
||||||
|
# Crear cliente de Anthropic
|
||||||
|
client = anthropic.Anthropic(api_key=ai_config.api_key)
|
||||||
|
|
||||||
|
# Antropic usa un formato diferente: system separado de messages
|
||||||
|
# El primer mensaje es el system prompt
|
||||||
|
system_content = messages[0]['content'] if messages[0]['role'] == 'system' else ""
|
||||||
|
user_messages = [msg for msg in messages if msg['role'] != 'system']
|
||||||
|
|
||||||
|
response = client.messages.create(
|
||||||
|
model=ai_config.model_name or "claude-sonnet-4-5",
|
||||||
|
max_tokens=max_tokens,
|
||||||
|
system=system_content,
|
||||||
|
messages=user_messages,
|
||||||
|
temperature=0.7
|
||||||
|
)
|
||||||
|
|
||||||
|
ai_response = response.content[0].text
|
||||||
|
confidence = 0.85
|
||||||
|
|
||||||
elif ai_config.provider == 'gemini':
|
elif ai_config.provider == 'gemini':
|
||||||
import google.generativeai as genai
|
import google.generativeai as genai
|
||||||
genai.configure(api_key=ai_config.api_key)
|
genai.configure(api_key=ai_config.api_key)
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ class InspectionDetail(Inspection):
|
|||||||
|
|
||||||
# AI Configuration Schemas
|
# AI Configuration Schemas
|
||||||
class AIConfigurationBase(BaseModel):
|
class AIConfigurationBase(BaseModel):
|
||||||
provider: str # openai, gemini
|
provider: str # openai, gemini, anthropic
|
||||||
api_key: str
|
api_key: str
|
||||||
model_name: Optional[str] = None
|
model_name: Optional[str] = None
|
||||||
logo_url: Optional[str] = None
|
logo_url: Optional[str] = None
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ passlib==1.7.4
|
|||||||
bcrypt==4.0.1
|
bcrypt==4.0.1
|
||||||
python-multipart==0.0.6
|
python-multipart==0.0.6
|
||||||
openai==1.10.0
|
openai==1.10.0
|
||||||
|
anthropic==0.40.0
|
||||||
google-generativeai==0.3.2
|
google-generativeai==0.3.2
|
||||||
Pillow==10.2.0
|
Pillow==10.2.0
|
||||||
reportlab==4.0.9
|
reportlab==4.0.9
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "checklist-frontend",
|
"name": "checklist-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.2.4",
|
"version": "1.2.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Service Worker para PWA con detección de actualizaciones
|
// Service Worker para PWA con detección de actualizaciones
|
||||||
// IMPORTANTE: Actualizar esta versión cada vez que se despliegue una nueva versión
|
// IMPORTANTE: Actualizar esta versión cada vez que se despliegue una nueva versión
|
||||||
const CACHE_NAME = 'ayutec-v1.2.4';
|
const CACHE_NAME = 'ayutec-v1.2.5';
|
||||||
const urlsToCache = [
|
const urlsToCache = [
|
||||||
'/',
|
'/',
|
||||||
'/index.html'
|
'/index.html'
|
||||||
|
|||||||
@@ -654,6 +654,15 @@ function SettingsTab({ user }) {
|
|||||||
<div className="font-semibold">OpenAI</div>
|
<div className="font-semibold">OpenAI</div>
|
||||||
<div className="text-xs text-gray-600 mt-1">GPT-4, GPT-4 Vision</div>
|
<div className="text-xs text-gray-600 mt-1">GPT-4, GPT-4 Vision</div>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setFormData({ ...formData, provider: 'anthropic', model_name: 'claude-sonnet-4-5' })}
|
||||||
|
className={`p-4 border-2 rounded-lg transition ${formData.provider === 'anthropic' ? 'border-purple-500 bg-purple-50' : 'border-gray-300 hover:border-gray-400'}`}
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-2">🧠</div>
|
||||||
|
<div className="font-semibold">Anthropic Claude</div>
|
||||||
|
<div className="text-xs text-gray-600 mt-1">Sonnet, Opus, Haiku</div>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setFormData({ ...formData, provider: 'gemini', model_name: 'gemini-2.5-pro' })}
|
onClick={() => setFormData({ ...formData, provider: 'gemini', model_name: 'gemini-2.5-pro' })}
|
||||||
@@ -669,19 +678,21 @@ function SettingsTab({ user }) {
|
|||||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">API Key</h3>
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">API Key</h3>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
{formData.provider === 'openai' ? 'OpenAI API Key' : 'Google AI API Key'}
|
{formData.provider === 'openai' ? 'OpenAI API Key' : formData.provider === 'anthropic' ? 'Anthropic API Key' : 'Google AI API Key'}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
value={formData.api_key}
|
value={formData.api_key}
|
||||||
onChange={(e) => setFormData({ ...formData, api_key: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, api_key: e.target.value })}
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
placeholder={formData.provider === 'openai' ? 'sk-...' : 'AIza...'}
|
placeholder={formData.provider === 'openai' ? 'sk-...' : formData.provider === 'anthropic' ? 'sk-ant-...' : 'AIza...'}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-500 mt-2">
|
<p className="text-xs text-gray-500 mt-2">
|
||||||
{formData.provider === 'openai' ? (
|
{formData.provider === 'openai' ? (
|
||||||
<>Obtén tu API key en <a href="https://platform.openai.com/api-keys" target="_blank" className="text-blue-600 hover:underline">OpenAI Platform</a></>
|
<>Obtén tu API key en <a href="https://platform.openai.com/api-keys" target="_blank" className="text-blue-600 hover:underline">OpenAI Platform</a></>
|
||||||
|
) : formData.provider === 'anthropic' ? (
|
||||||
|
<>Obtén tu API key en <a href="https://console.anthropic.com/" target="_blank" className="text-purple-600 hover:underline">Anthropic Console</a></>
|
||||||
) : (
|
) : (
|
||||||
<>Obtén tu API key en <a href="https://makersuite.google.com/app/apikey" target="_blank" className="text-blue-600 hover:underline">Google AI Studio</a></>
|
<>Obtén tu API key en <a href="https://makersuite.google.com/app/apikey" target="_blank" className="text-blue-600 hover:underline">Google AI Studio</a></>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export default function Sidebar({ user, activeTab, setActiveTab, sidebarOpen, se
|
|||||||
className="w-10 h-10 object-contain bg-white rounded p-1"
|
className="w-10 h-10 object-contain bg-white rounded p-1"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-indigo-300 font-medium hover:text-indigo-200">
|
<p className="text-xs text-indigo-300 font-medium hover:text-indigo-200">
|
||||||
Ayutec v1.2.4
|
Ayutec v1.2.5
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user