Tu Nombre commited on
Commit
13c15b7
·
1 Parent(s): abd1ef4

Initial commit for Papalia3 Space

Browse files
Files changed (6) hide show
  1. .DS_Store +0 -0
  2. Dockerfile +38 -36
  3. README.md +1 -1
  4. app.py +15 -49
  5. requirements.txt +1 -1
  6. templates/index.html +21 -36
.DS_Store CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
 
Dockerfile CHANGED
@@ -1,10 +1,9 @@
 
1
  FROM python:3.9
2
 
3
- # Install Ollama and required tools
4
  RUN apt-get update && apt-get install -y curl wget netcat-traditional && \
5
  curl -fsSL https://ollama.com/install.sh | sh
6
 
7
- # Create non-root user
8
  RUN useradd -m -u 1000 user
9
 
10
  WORKDIR /app
@@ -12,41 +11,45 @@ WORKDIR /app
12
  COPY --chown=user ./requirements.txt requirements.txt
13
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
 
15
- # Copy application files including templates
16
  COPY --chown=user . /app
17
 
18
- # Create Modelfile as a separate step
19
- RUN printf "FROM llama3\n\nSYSTEM \"Eres un asistente especializado en Desarrollo Humano, basado en la duodécima edición del libro de Papalia. Tu objetivo es proporcionar información precisa y actualizada sobre el desarrollo humano a lo largo del ciclo vital. Base tu conocimiento en la obra de Diane E. Papalia, Ruth Duskin Feldman y Gabriela Martorell. Utiliza siempre evidencia científica y ejemplos del libro para respaldar tus respuestas.\"\n\nPARAMETER temperature 0.7\nPARAMETER top_k 40\nPARAMETER top_p 0.7\nPARAMETER repeat_penalty 1.1" > /app/Modelfile
20
-
21
- # Create startup script
22
- RUN printf '#!/bin/bash\n\
23
- echo "Configurando sistema..."\n\
24
- ulimit -v unlimited\n\
25
- \n\
26
- echo "Iniciando servidor Ollama..."\n\
27
- ollama serve &\n\
28
- \n\
29
- echo "Esperando a que Ollama esté listo..."\n\
30
- for i in {1..120}; do\n\
31
- if nc -z localhost 11434; then\n\
32
- echo "Ollama está listo"\n\
33
- break\n\
34
- fi\n\
35
- echo "Esperando a Ollama... $i/120s"\n\
36
- sleep 1\n\
37
- done\n\
38
- \n\
39
- echo "Creando modelo..."\n\
40
- cd /app\n\
41
- ollama create llama3.2:1b-papalia -f Modelfile\n\
42
- \n\
43
- echo "Verificando modelo..."\n\
44
- if ! ollama list | grep -q "llama3.2:1b-papalia"; then\n\
45
- echo "Error: El modelo no se pudo crear"\n\
46
- exit 1\n\
47
- fi\n\
48
- \n\
49
- echo "Iniciando API..."\n\
 
 
 
 
 
50
  exec uvicorn app:app --host 0.0.0.0 --port 7860 --timeout-keep-alive 120' > /app/start.sh
51
 
52
  RUN chmod +x /app/start.sh
@@ -54,7 +57,6 @@ RUN chmod +x /app/start.sh
54
  USER user
55
  ENV PATH="/home/user/.local/bin:$PATH"
56
 
57
- # Health check with increased interval
58
  HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=3 \
59
  CMD curl -f http://localhost:7860/health || exit 1
60
 
 
1
+ # Dockerfile
2
  FROM python:3.9
3
 
 
4
  RUN apt-get update && apt-get install -y curl wget netcat-traditional && \
5
  curl -fsSL https://ollama.com/install.sh | sh
6
 
 
7
  RUN useradd -m -u 1000 user
8
 
9
  WORKDIR /app
 
11
  COPY --chown=user ./requirements.txt requirements.txt
12
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
13
 
 
14
  COPY --chown=user . /app
15
 
16
+ RUN echo 'FROM llama3
17
+
18
+ SYSTEM "Eres un asistente especializado en Desarrollo Humano, basado en la duodécima edición del libro de Papalia. Tu objetivo es proporcionar información precisa y actualizada sobre el desarrollo humano a lo largo del ciclo vital. Base tu conocimiento en la obra de Diane E. Papalia, Ruth Duskin Feldman y Gabriela Martorell. Utiliza siempre evidencia científica y ejemplos del libro para respaldar tus respuestas."
19
+
20
+ PARAMETER temperature 0.7
21
+ PARAMETER top_k 40
22
+ PARAMETER top_p 0.7
23
+ PARAMETER repeat_penalty 1.1' > /app/Modelfile
24
+
25
+ RUN printf '#!/bin/bash
26
+ echo "Configurando sistema..."
27
+ ulimit -v unlimited
28
+
29
+ echo "Iniciando servidor Ollama..."
30
+ ollama serve &
31
+
32
+ echo "Esperando a que Ollama esté listo..."
33
+ for i in {1..120}; do
34
+ if nc -z localhost 11434; then
35
+ echo "Ollama está listo"
36
+ break
37
+ fi
38
+ echo "Esperando a Ollama... $i/120s"
39
+ sleep 1
40
+ done
41
+
42
+ echo "Creando modelo..."
43
+ cd /app
44
+ ollama create llama3.2:1b-papalia -f Modelfile
45
+
46
+ echo "Verificando modelo..."
47
+ if ! ollama list | grep -q "llama3.2:1b-papalia"; then
48
+ echo "Error: El modelo no se pudo crear"
49
+ exit 1
50
+ fi
51
+
52
+ echo "Iniciando API..."
53
  exec uvicorn app:app --host 0.0.0.0 --port 7860 --timeout-keep-alive 120' > /app/start.sh
54
 
55
  RUN chmod +x /app/start.sh
 
57
  USER user
58
  ENV PATH="/home/user/.local/bin:$PATH"
59
 
 
60
  HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=3 \
61
  CMD curl -f http://localhost:7860/health || exit 1
62
 
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: Llama3 Papalia Nuevo
3
  emoji: 🏃
4
  colorFrom: red
5
  colorTo: purple
 
1
  ---
2
+ title: Papali3
3
  emoji: 🏃
4
  colorFrom: red
5
  colorTo: purple
app.py CHANGED
@@ -1,11 +1,9 @@
1
  from fastapi import FastAPI, HTTPException, Request
2
  from fastapi.responses import HTMLResponse, JSONResponse
3
- from fastapi.staticfiles import StaticFiles
4
  from fastapi.templating import Jinja2Templates
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from pydantic import BaseModel
7
  import httpx
8
- import os
9
  import logging
10
  from typing import Optional, Dict, Any
11
 
@@ -13,19 +11,12 @@ logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
  app = FastAPI(
16
- title="Llama3-Papalia Inference API & UI",
17
- description="API para interactuar con el modelo Llama3-Papalia",
18
  version="1.0.0"
19
  )
20
 
21
- app.add_middleware(
22
- CORSMiddleware,
23
- allow_origins=["*"],
24
- allow_credentials=True,
25
- allow_methods=["*"],
26
- allow_headers=["*"],
27
- )
28
-
29
  templates = Jinja2Templates(directory="templates")
30
 
31
  class QueryRequest(BaseModel):
@@ -45,11 +36,10 @@ async def check_ollama_status() -> Dict[str, Any]:
45
  async with httpx.AsyncClient(timeout=5.0) as client:
46
  response = await client.get(OLLAMA_BASE_URL)
47
  if response.status_code != 200:
48
- return {"status": "error", "message": "Ollama no responde", "code": response.status_code}
49
 
50
- # Verificar que el modelo esté disponible
51
  model_response = await client.post(
52
- OLLAMA_BASE_URL + "/api/generate",
53
  json={
54
  "model": "llama3.2:1b-papalia",
55
  "prompt": "test",
@@ -57,38 +47,25 @@ async def check_ollama_status() -> Dict[str, Any]:
57
  },
58
  timeout=5.0
59
  )
60
-
61
  if model_response.status_code != 200:
62
- return {"status": "error", "message": "Modelo no disponible", "code": model_response.status_code}
63
-
64
- return {"status": "ok", "message": "Servicio funcionando correctamente"}
65
  except Exception as e:
66
  return {"status": "error", "message": str(e)}
67
 
68
  @app.get("/", response_class=HTMLResponse)
69
  async def read_root(request: Request):
70
  status = await check_ollama_status()
71
- return templates.TemplateResponse(
72
- "index.html",
73
- {
74
- "request": request,
75
- "title": "Llama3-Papalia Inference",
76
- "status": status
77
- }
78
- )
79
 
80
  @app.post("/generate")
81
  async def generate_response(query: QueryRequest):
82
- logger.info(f"Recibida solicitud: {query.prompt[:50]}...")
83
-
84
  try:
85
  async with httpx.AsyncClient(timeout=60.0) as client:
86
  status = await check_ollama_status()
87
  if status["status"] != "ok":
88
- raise HTTPException(
89
- status_code=503,
90
- detail=status["message"]
91
- )
92
 
93
  response = await client.post(
94
  OLLAMA_API_URL,
@@ -103,31 +80,20 @@ async def generate_response(query: QueryRequest):
103
  )
104
 
105
  if response.status_code != 200:
106
- raise HTTPException(
107
- status_code=response.status_code,
108
- detail=f"Error del modelo: {response.text}"
109
- )
110
 
111
  result = response.json()
112
  logger.info("Respuesta generada exitosamente")
113
  return {"response": result.get("response", ""), "model": "llama3.2:1b-papalia"}
114
 
115
  except httpx.TimeoutException:
116
- logger.error("Timeout en la solicitud a Ollama")
117
- raise HTTPException(
118
- status_code=504,
119
- detail="Timeout en la solicitud al modelo"
120
- )
121
  except Exception as e:
122
  logger.error(f"Error: {str(e)}")
123
- raise HTTPException(
124
- status_code=500,
125
- detail=str(e)
126
- )
127
 
128
  @app.get("/health")
129
  async def health_check():
130
  status = await check_ollama_status()
131
- if status["status"] == "ok":
132
- return {"status": "healthy", "message": status["message"]}
133
- return {"status": "unhealthy", "error": status["message"]}
 
1
  from fastapi import FastAPI, HTTPException, Request
2
  from fastapi.responses import HTMLResponse, JSONResponse
 
3
  from fastapi.templating import Jinja2Templates
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from pydantic import BaseModel
6
  import httpx
 
7
  import logging
8
  from typing import Optional, Dict, Any
9
 
 
11
  logger = logging.getLogger(__name__)
12
 
13
  app = FastAPI(
14
+ title="Llama3.2:1b-Papalia Inference API",
15
+ description="API para interactuar con el modelo especializado en Desarrollo Humano",
16
  version="1.0.0"
17
  )
18
 
19
+ app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
 
 
 
 
 
 
 
20
  templates = Jinja2Templates(directory="templates")
21
 
22
  class QueryRequest(BaseModel):
 
36
  async with httpx.AsyncClient(timeout=5.0) as client:
37
  response = await client.get(OLLAMA_BASE_URL)
38
  if response.status_code != 200:
39
+ return {"status": "error", "message": "Ollama no responde"}
40
 
 
41
  model_response = await client.post(
42
+ OLLAMA_API_URL,
43
  json={
44
  "model": "llama3.2:1b-papalia",
45
  "prompt": "test",
 
47
  },
48
  timeout=5.0
49
  )
 
50
  if model_response.status_code != 200:
51
+ return {"status": "error", "message": "Modelo no disponible"}
52
+ return {"status": "ok", "message": "Servicio activo"}
 
53
  except Exception as e:
54
  return {"status": "error", "message": str(e)}
55
 
56
  @app.get("/", response_class=HTMLResponse)
57
  async def read_root(request: Request):
58
  status = await check_ollama_status()
59
+ return templates.TemplateResponse("index.html", {"request": request, "title": "Papalia3 Inference", "status": status})
 
 
 
 
 
 
 
60
 
61
  @app.post("/generate")
62
  async def generate_response(query: QueryRequest):
63
+ logger.info(f"Solicitud recibida: {query.prompt[:50]}...")
 
64
  try:
65
  async with httpx.AsyncClient(timeout=60.0) as client:
66
  status = await check_ollama_status()
67
  if status["status"] != "ok":
68
+ raise HTTPException(status_code=503, detail=status["message"])
 
 
 
69
 
70
  response = await client.post(
71
  OLLAMA_API_URL,
 
80
  )
81
 
82
  if response.status_code != 200:
83
+ raise HTTPException(status_code=response.status_code, detail=f"Error del modelo: {response.text}")
 
 
 
84
 
85
  result = response.json()
86
  logger.info("Respuesta generada exitosamente")
87
  return {"response": result.get("response", ""), "model": "llama3.2:1b-papalia"}
88
 
89
  except httpx.TimeoutException:
90
+ logger.error("Timeout en solicitud a Ollama")
91
+ raise HTTPException(status_code=504, detail="Timeout en solicitud al modelo")
 
 
 
92
  except Exception as e:
93
  logger.error(f"Error: {str(e)}")
94
+ raise HTTPException(status_code=500, detail=str(e))
 
 
 
95
 
96
  @app.get("/health")
97
  async def health_check():
98
  status = await check_ollama_status()
99
+ return {"status": "healthy", "message": status["message"]} if status["status"] == "ok" else {"status": "unhealthy", "error": status["message"]}
 
 
requirements.txt CHANGED
@@ -4,4 +4,4 @@ python-dotenv==1.0.0
4
  httpx==0.25.2
5
  pydantic==2.5.2
6
  jinja2==3.1.2
7
- python-multipart==0.0.6
 
4
  httpx==0.25.2
5
  pydantic==2.5.2
6
  jinja2==3.1.2
7
+ python-multipart==0.0.6
templates/index.html CHANGED
@@ -1,33 +1,30 @@
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
- <title>Llama3-Papalia Inference</title>
5
  <script src="https://cdn.tailwindcss.com"></script>
 
6
  </head>
7
- <body class="bg-gray-100 p-8">
8
  <div class="max-w-4xl mx-auto">
9
  <div class="flex justify-between items-center mb-8">
10
- <h1 class="text-3xl font-bold">Llama3-Papalia Inference</h1>
11
  <div id="service-status" class="text-sm">Verificando estado...</div>
12
  </div>
13
 
14
- <div class="bg-white rounded-lg shadow-md p-6">
15
  <div class="mb-4">
16
- <label class="block text-gray-700 text-sm font-bold mb-2" for="prompt">
17
- Prompt
18
- </label>
19
  <textarea
20
  id="prompt"
21
  class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
22
  rows="4"
23
- placeholder="Escribe tu pregunta aquí..."></textarea>
24
  </div>
25
 
26
  <div class="grid grid-cols-2 gap-4 mb-4">
27
  <div>
28
- <label class="block text-gray-700 text-sm font-bold mb-2" for="temperature">
29
- Temperature
30
- </label>
31
  <input
32
  type="number"
33
  id="temperature"
@@ -38,9 +35,7 @@
38
  class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
39
  </div>
40
  <div>
41
- <label class="block text-gray-700 text-sm font-bold mb-2" for="max_tokens">
42
- Max Tokens
43
- </label>
44
  <input
45
  type="number"
46
  id="max_tokens"
@@ -58,19 +53,14 @@
58
  class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline disabled:opacity-50">
59
  Generar Respuesta
60
  </button>
61
- <div id="loading" class="hidden">
62
  Generando...
63
  </div>
64
  </div>
65
 
66
- <div class="mt-8">
67
- <label class="block text-gray-700 text-sm font-bold mb-2">
68
- Respuesta
69
- </label>
70
- <div
71
- id="response"
72
- class="mt-2 p-4 bg-gray-100 rounded min-h-[200px] whitespace-pre-wrap">
73
- </div>
74
  </div>
75
  </div>
76
  </div>
@@ -81,20 +71,21 @@
81
  const response = await fetch('/health');
82
  const data = await response.json();
83
  const statusEl = document.getElementById('service-status');
 
84
 
85
  if (data.status === 'healthy') {
86
  statusEl.textContent = '✅ Servicio activo';
87
- statusEl.className = 'text-green-600';
88
- document.getElementById('generate-button').disabled = false;
89
  } else {
90
  statusEl.textContent = '❌ ' + (data.error || 'Servicio no disponible');
91
- statusEl.className = 'text-red-600';
92
- document.getElementById('generate-button').disabled = true;
93
  }
94
  } catch (error) {
95
  const statusEl = document.getElementById('service-status');
96
  statusEl.textContent = '❌ Error de conexión';
97
- statusEl.className = 'text-red-600';
98
  document.getElementById('generate-button').disabled = true;
99
  }
100
  }
@@ -119,9 +110,7 @@
119
  try {
120
  const response = await fetch('/generate', {
121
  method: 'POST',
122
- headers: {
123
- 'Content-Type': 'application/json',
124
- },
125
  body: JSON.stringify({
126
  prompt: promptEl.value,
127
  temperature: parseFloat(temperatureEl.value),
@@ -130,10 +119,7 @@
130
  });
131
 
132
  const data = await response.json();
133
-
134
- if (!response.ok) {
135
- throw new Error(data.detail || 'Error en la generación de respuesta');
136
- }
137
 
138
  responseEl.textContent = data.response || 'No se recibió respuesta del modelo';
139
  responseEl.className = 'mt-2 p-4 bg-gray-100 rounded min-h-[200px] whitespace-pre-wrap';
@@ -146,7 +132,6 @@
146
  }
147
  }
148
 
149
- // Verificar estado cada 30 segundos
150
  checkHealth();
151
  setInterval(checkHealth, 30000);
152
  </script>
 
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
+ <title>Papalia3 Inference</title>
5
  <script src="https://cdn.tailwindcss.com"></script>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  </head>
8
+ <body class="bg-gray-100 p-4 md:p-8">
9
  <div class="max-w-4xl mx-auto">
10
  <div class="flex justify-between items-center mb-8">
11
+ <h1 class="text-2xl md:text-3xl font-bold">Papalia3 - Desarrollo Humano</h1>
12
  <div id="service-status" class="text-sm">Verificando estado...</div>
13
  </div>
14
 
15
+ <div class="bg-white rounded-lg shadow-md p-4 md:p-6">
16
  <div class="mb-4">
17
+ <label class="block text-gray-700 text-sm font-bold mb-2" for="prompt">Prompt</label>
 
 
18
  <textarea
19
  id="prompt"
20
  class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
21
  rows="4"
22
+ placeholder="Escribe tu pregunta sobre Desarrollo Humano..."></textarea>
23
  </div>
24
 
25
  <div class="grid grid-cols-2 gap-4 mb-4">
26
  <div>
27
+ <label class="block text-gray-700 text-sm font-bold mb-2" for="temperature">Temperature</label>
 
 
28
  <input
29
  type="number"
30
  id="temperature"
 
35
  class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
36
  </div>
37
  <div>
38
+ <label class="block text-gray-700 text-sm font-bold mb-2" for="max_tokens">Max Tokens</label>
 
 
39
  <input
40
  type="number"
41
  id="max_tokens"
 
53
  class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline disabled:opacity-50">
54
  Generar Respuesta
55
  </button>
56
+ <div id="loading" class="hidden text-gray-600">
57
  Generando...
58
  </div>
59
  </div>
60
 
61
+ <div class="mt-6">
62
+ <label class="block text-gray-700 text-sm font-bold mb-2">Respuesta</label>
63
+ <div id="response" class="mt-2 p-4 bg-gray-100 rounded min-h-[200px] whitespace-pre-wrap"></div>
 
 
 
 
 
64
  </div>
65
  </div>
66
  </div>
 
71
  const response = await fetch('/health');
72
  const data = await response.json();
73
  const statusEl = document.getElementById('service-status');
74
+ const buttonEl = document.getElementById('generate-button');
75
 
76
  if (data.status === 'healthy') {
77
  statusEl.textContent = '✅ Servicio activo';
78
+ statusEl.className = 'text-sm text-green-600';
79
+ buttonEl.disabled = false;
80
  } else {
81
  statusEl.textContent = '❌ ' + (data.error || 'Servicio no disponible');
82
+ statusEl.className = 'text-sm text-red-600';
83
+ buttonEl.disabled = true;
84
  }
85
  } catch (error) {
86
  const statusEl = document.getElementById('service-status');
87
  statusEl.textContent = '❌ Error de conexión';
88
+ statusEl.className = 'text-sm text-red-600';
89
  document.getElementById('generate-button').disabled = true;
90
  }
91
  }
 
110
  try {
111
  const response = await fetch('/generate', {
112
  method: 'POST',
113
+ headers: {'Content-Type': 'application/json'},
 
 
114
  body: JSON.stringify({
115
  prompt: promptEl.value,
116
  temperature: parseFloat(temperatureEl.value),
 
119
  });
120
 
121
  const data = await response.json();
122
+ if (!response.ok) throw new Error(data.detail || 'Error en la generación');
 
 
 
123
 
124
  responseEl.textContent = data.response || 'No se recibió respuesta del modelo';
125
  responseEl.className = 'mt-2 p-4 bg-gray-100 rounded min-h-[200px] whitespace-pre-wrap';
 
132
  }
133
  }
134
 
 
135
  checkHealth();
136
  setInterval(checkHealth, 30000);
137
  </script>