apex / app.py
Mution's picture
Update app.py
1b20ee9 verified
Raw
History Blame Contribute Delete
14.9 kB
import os
import sys
import threading
from flask import Flask, request, Response, stream_with_context, jsonify, render_template_string
from flask_cors import CORS
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer, BitsAndBytesConfig
import torch
print("LOG: Iniciando Motor Hydra 3.0 (Docker API - Hydra Software)...")
sys.stdout.flush()
app = Flask(__name__)
CORS(app)
model_id = "Mution/Hydra-AI-3B-Coder-Pro"
token = os.getenv("HF_TOKEN")
VALID_API_KEYS = {
"hydra-sk-master-9928374": "Master_Admin",
"hydra-sk-root-1122334": "Root_System",
"hydra-sk-dev-5566778": "Developer_Terminal",
"hydra-sk-bot-alpha-1029384": "Discord_Bot_Alpha",
"hydra-sk-bot-beta-2039485": "Discord_Bot_Beta",
"hydra-sk-bot-gamma-3049586": "Telegram_Bot",
"hydra-sk-bot-delta-4059687": "Twitch_Bot",
"hydra-sk-panel-main-5647382": "Web_Panel_Main",
"hydra-sk-panel-sec-6758493": "Web_Panel_Secundario",
"hydra-sk-panel-event-7869504": "Event_Dashboard",
"hydra-sk-vip-001-8970615": "VIP_User_1",
"hydra-sk-vip-002-9081726": "VIP_User_2",
"hydra-sk-vip-003-0192837": "VIP_User_3",
"hydra-sk-test-1203948": "Testing_Key_A",
"hydra-sk-test-2314059": "Testing_Key_B",
"hydra-sk-node-001-110022": "Node_Server_1",
"hydra-sk-node-002-334455": "Node_Server_2",
"hydra-sk-node-003-667788": "Node_Server_3",
"hydra-sk-node-004-990011": "Node_Server_4",
"hydra-sk-node-005-223344": "Node_Server_5",
"hydra-sk-app-mobile-1010": "Mobile_App_iOS",
"hydra-sk-app-mobile-2020": "Mobile_App_Android",
"hydra-sk-app-desktop-3030": "Desktop_App_Electron",
"hydra-sk-app-web-4040": "Web_Client_App",
"hydra-sk-service-auth-01": "Auth_Service",
"hydra-sk-service-db-02": "Database_Worker",
"hydra-sk-service-cache-03": "Redis_Cache_Worker",
"hydra-sk-service-queue-04": "RabbitMQ_Worker",
"hydra-sk-service-cron-05": "Cron_Task_Manager",
"hydra-sk-client-pro-1001": "Client_Pro_Tier_1",
"hydra-sk-client-pro-1002": "Client_Pro_Tier_2",
"hydra-sk-client-pro-1003": "Client_Pro_Tier_3",
"hydra-sk-client-pro-1004": "Client_Pro_Tier_4",
"hydra-sk-client-pro-1005": "Client_Pro_Tier_5",
"hydra-sk-client-enterprise-2001": "Enterprise_Client_A",
"hydra-sk-client-enterprise-2002": "Enterprise_Client_B",
"hydra-sk-client-enterprise-2003": "Enterprise_Client_C",
"hydra-sk-internal-tool-01": "Internal_Dev_Tool_1",
"hydra-sk-internal-tool-02": "Internal_Dev_Tool_2",
"hydra-sk-internal-tool-03": "Internal_Dev_Tool_3",
"hydra-sk-bot-v2-01": "Discord_Bot_v2_1",
"hydra-sk-bot-v2-02": "Discord_Bot_v2_2",
"hydra-sk-bot-v2-03": "Discord_Bot_v2_3",
"hydra-sk-bot-v2-04": "Discord_Bot_v2_4",
"hydra-sk-bot-v2-05": "Discord_Bot_v2_5",
"hydra-sk-integration-gh-01": "GitHub_Actions_Integration",
"hydra-sk-integration-gl-02": "GitLab_CI_Integration",
"hydra-sk-integration-docker-03": "Docker_Hub_Integration",
"hydra-sk-integration-aws-04": "AWS_Lambda_Integration",
"hydra-sk-integration-gcf-05": "GCP_Cloud_Functions",
"hydra-sk-vip-004-1234567": "VIP_User_4",
"hydra-sk-vip-005-2345678": "VIP_User_5",
"hydra-sk-vip-006-3456789": "VIP_User_6",
"hydra-sk-vip-007-4567890": "VIP_User_7",
"hydra-sk-vip-008-5678901": "VIP_User_8",
"hydra-sk-vip-009-6789012": "VIP_User_9",
"hydra-sk-vip-010-7890123": "VIP_User_10",
"hydra-sk-sec-audit-01": "Security_Audit_Tool_A",
"hydra-sk-sec-audit-02": "Security_Audit_Tool_B",
"hydra-sk-monitoring-01": "Prometheus_Monitoring",
"hydra-sk-monitoring-02": "Grafana_Dashboard_Agent",
"hydra-sk-ext-partner-01": "External_Partner_Alpha",
"hydra-sk-ext-partner-02": "External_Partner_Beta",
"hydra-sk-ext-partner-03": "External_Partner_Gamma",
"hydra-sk-sandbox-01": "Sandbox_Environment_1",
"hydra-sk-sandbox-02": "Sandbox_Environment_2",
"hydra-sk-sandbox-03": "Sandbox_Environment_3",
"hydra-sk-cluster-east-01": "Cluster_US_East_1",
"hydra-sk-cluster-east-02": "Cluster_US_East_2",
"hydra-sk-cluster-west-01": "Cluster_US_West_1",
"hydra-sk-cluster-eu-01": "Cluster_EU_Central_1",
"hydra-sk-cluster-sa-01": "Cluster_SA_East_1",
"hydra-sk-backup-01": "Backup_Worker_Node_1",
"hydra-sk-backup-02": "Backup_Worker_Node_2",
"hydra-sk-legacy-01": "Legacy_System_Adapter_1",
"hydra-sk-legacy-02": "Legacy_System_Adapter_2",
"hydra-sk-agent-ai-01": "AI_Agent_Code_Reviewer",
"hydra-sk-agent-ai-02": "AI_Agent_Bug_Fixer",
"hydra-sk-agent-ai-03": "AI_Agent_Refactorer",
"hydra-sk-agent-ai-04": "AI_Agent_Doc_Generator",
"hydra-sk-agent-ai-05": "AI_Agent_Unit_Tester",
"hydra-sk-gateway-01": "API_Gateway_Primary",
"hydra-sk-gateway-02": "API_Gateway_Secondary",
"hydra-sk-gateway-03": "API_Gateway_Failover",
"hydra-sk-custom-9001": "Custom_Deployment_Key_1",
"hydra-sk-custom-9002": "Custom_Deployment_Key_2",
"hydra-sk-custom-9003": "Custom_Deployment_Key_3",
"hydra-sk-custom-9004": "Custom_Deployment_Key_4",
"hydra-sk-custom-9005": "Custom_Deployment_Key_5",
"hydra-sk-custom-9006": "Custom_Deployment_Key_6",
"hydra-sk-custom-9007": "Custom_Deployment_Key_7",
"hydra-sk-custom-9008": "Custom_Deployment_Key_8",
"hydra-sk-custom-9009": "Custom_Deployment_Key_9",
"hydra-sk-custom-9010": "Custom_Deployment_Key_10"
}
try:
print("LOG: Cargando Tokenizador Hydra 3B...")
tokenizer = AutoTokenizer.from_pretrained(model_id, token=token)
print("LOG: Configurando cuantizaci贸n 4-bit...")
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
llm_int8_enable_fp32_cpu_offload=True
)
print("LOG: Cargando modelo fusionado Hydra 3B...")
model = AutoModelForCausalLM.from_pretrained(
model_id,
token=token,
quantization_config=bnb_config,
device_map="auto",
torch_dtype=torch.float16,
low_cpu_mem_usage=True,
offload_folder="offload_dir"
)
print("LOG: IA cargada y lista.")
except Exception as e:
print(f"LOG CR脥TICO: Error al cargar el modelo: {e}")
sys.exit(1)
HTML_PAGE = """
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hydra AI 3B - Private Console</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-main: #0b0f19;
--bg-card: #111827;
--bg-chat: #070a12;
--border-color: #1f293d;
--accent-cyan: #06b6d4;
--accent-emerald: #10b981;
--text-main: #f3f4f6;
--text-muted: #9ca3af;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-main);
color: var(--text-main);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.wrapper {
width: 100%;
max-width: 1100px;
height: 90vh;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 16px;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 20px 50px rgba(0,0,0,0.6);
}
.top-bar {
padding: 16px 24px;
background: #162032;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.brand-title {
display: flex;
align-items: center;
gap: 10px;
font-weight: 700;
font-size: 1.15rem;
color: var(--accent-cyan);
letter-spacing: 0.5px;
}
.status-badge {
background: rgba(16, 185, 129, 0.15);
color: var(--accent-emerald);
border: 1px solid rgba(16, 185, 129, 0.3);
padding: 4px 10px;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
}
.key-auth-box {
display: flex;
align-items: center;
gap: 10px;
}
.key-auth-box input {
background: var(--bg-chat);
border: 1px solid var(--border-color);
color: var(--accent-cyan);
padding: 8px 12px;
border-radius: 8px;
font-family: 'Fira Code', monospace;
font-size: 0.85rem;
width: 260px;
outline: none;
}
.key-auth-box input:focus {
border-color: var(--accent-cyan);
}
.chat-area {
flex: 1;
background: var(--bg-chat);
padding: 24px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
}
.message {
padding: 14px 18px;
border-radius: 10px;
max-width: 85%;
font-size: 0.95rem;
line-height: 1.6;
white-space: pre-wrap;
word-break: break-word;
}
.message.user {
background: #1d4ed8;
color: #ffffff;
align-self: flex-end;
border-bottom-right-radius: 2px;
}
.message.assistant {
background: #1e293b;
color: #e2e8f0;
align-self: flex-start;
border-left: 4px solid var(--accent-cyan);
border-bottom-left-radius: 2px;
font-family: 'Fira Code', monospace;
}
.controls-area {
padding: 16px 24px;
background: #162032;
border-top: 1px solid var(--border-color);
display: flex;
gap: 12px;
}
textarea {
flex: 1;
background: var(--bg-chat);
border: 1px solid var(--border-color);
border-radius: 10px;
color: #f3f4f6;
padding: 12px;
font-family: 'Inter', sans-serif;
font-size: 0.95rem;
resize: none;
height: 52px;
outline: none;
}
textarea:focus {
border-color: var(--accent-cyan);
}
button {
background: var(--accent-cyan);
color: #070a12;
border: none;
border-radius: 10px;
padding: 0 24px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s ease;
}
button:hover {
background: #0891b2;
color: #ffffff;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="top-bar">
<div class="brand-title">
HYDRA AI 3B <span class="status-badge">ONLINE</span>
</div>
<div class="key-auth-box">
<input type="password" id="apiKey" placeholder="Ingrese x-api-key para autenticar..." />
</div>
</div>
<div class="chat-area" id="chat">
<div class="message assistant">Consola privada activa. Ingrese su API Key en la parte superior para consultar al motor de IA.</div>
</div>
<div class="controls-area">
<textarea id="promptInput" placeholder="Escriba su consulta o instrucci贸n de c贸digo..."></textarea>
<button onclick="sendPrompt()">Enviar</button>
</div>
</div>
<script>
async function sendPrompt() {
const apiKeyInput = document.getElementById('apiKey').value.trim();
const promptInput = document.getElementById('promptInput');
const chat = document.getElementById('chat');
const userText = promptInput.value.trim();
if (!apiKeyInput) {
alert('Ingrese una API Key v谩lida en la barra superior.');
return;
}
if (!userText) return;
const userMsgDiv = document.createElement('div');
userMsgDiv.className = 'message user';
userMsgDiv.textContent = userText;
chat.appendChild(userMsgDiv);
promptInput.value = '';
chat.scrollTop = chat.scrollHeight;
const assistantMsgDiv = document.createElement('div');
assistantMsgDiv.className = 'message assistant';
assistantMsgDiv.textContent = '';
chat.appendChild(assistantMsgDiv);
try {
const response = await fetch('/ask', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKeyInput
},
body: JSON.stringify({ prompt: userText })
});
if (!response.ok) {
const errData = await response.json().catch(() => ({}));
assistantMsgDiv.textContent = `Error (${response.status}): ${errData.error || 'Acceso Denegado'}`;
return;
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
assistantMsgDiv.textContent += decoder.decode(value, { stream: true });
chat.scrollTop = chat.scrollHeight;
}
} catch (err) {
assistantMsgDiv.textContent = 'Error de conexi贸n con el servidor.';
}
}
document.getElementById('promptInput').addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendPrompt();
}
});
</script>
</body>
</html>
"""
@app.route('/ask', methods=['POST'])
def ask():
api_key = request.headers.get("x-api-key")
if not api_key or api_key not in VALID_API_KEYS:
return jsonify({"error": "Acceso denegado. Llave invalida o inexistente."}), 401
try:
data = request.json
user_prompt = data.get("prompt", "")
prompt = f"<|im_start|>system\nEres Hydra 3.0, el motor de inteligencia artificial de Hydra Software. Eres un experto absoluto en programaci贸n y desarrollo de sistemas backend, bots y paneles web.<|im_end|>\n<|im_start|>user\n{user_prompt}<|im_end|>\n<|im_start|>assistant\n"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
def generate_task():
with torch.no_grad():
model.generate(
**inputs,
max_new_tokens=4096,
do_sample=True,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.15,
streamer=streamer
)
thread = threading.Thread(target=generate_task)
thread.start()
def generate_stream():
for text in streamer:
yield text
return Response(stream_with_context(generate_stream()), mimetype='text/plain')
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/', methods=['GET'])
def health():
return render_template_string(HTML_PAGE)
if __name__ == '__main__':
print("LOG: Iniciando servidor Flask en 0.0.0.0:7860")
app.run(host='0.0.0.0', port=7860, threaded=True)