jeanmarcocruz207 commited on
Commit
7d0a50b
·
verified ·
1 Parent(s): 64f7f86

Upload 29 files

Browse files
Files changed (2) hide show
  1. README.md +1 -0
  2. ollama_utils.py +114 -5
README.md CHANGED
@@ -151,6 +151,7 @@ Si el modelo no descarga, verifica el formato:
151
  3. Si quieres precargar un modelo para evitar descargas manuales, añade la variable `OLLAMA_AUTO_PULL=hf.co/usuario/modelo[:quant]` en los _secrets_ del Space.
152
  4. Usa `LLAMA_DEV_OLLAMA_URL` si expones un Ollama externo y `OLLAMA_WAIT_TIMEOUT` si necesitas ampliar el timeout en hardware lento.
153
  5. Cuando algo falle, revisa `/tmp/ollama.log` en los logs del Space: el entrypoint lo imprime antes de salir.
 
154
 
155
  ### 🚨 Notas Importantes
156
 
 
151
  3. Si quieres precargar un modelo para evitar descargas manuales, añade la variable `OLLAMA_AUTO_PULL=hf.co/usuario/modelo[:quant]` en los _secrets_ del Space.
152
  4. Usa `LLAMA_DEV_OLLAMA_URL` si expones un Ollama externo y `OLLAMA_WAIT_TIMEOUT` si necesitas ampliar el timeout en hardware lento.
153
  5. Cuando algo falle, revisa `/tmp/ollama.log` en los logs del Space: el entrypoint lo imprime antes de salir.
154
+ 6. En el runtime “Gradio” clásico tampoco tenés que instalar nada: `app.py` descarga automáticamente un binario portátil de Ollama (se guarda en `~/.local/ollama-lite`) si no detecta uno en el sistema y lo levanta antes de iniciar la UI.
155
 
156
  ### 🚨 Notas Importantes
157
 
ollama_utils.py CHANGED
@@ -1,8 +1,14 @@
1
  import json
2
  import hashlib
 
 
 
3
  import subprocess
 
 
4
  import time
5
  from collections import OrderedDict
 
6
  from typing import Dict, Optional, List, Tuple, Generator, Any
7
 
8
  import requests
@@ -35,11 +41,105 @@ class LRUCache:
35
 
36
  response_cache = LRUCache(settings.CACHE_MAX_ITEMS)
37
 
 
 
 
 
 
38
 
39
  def _ollama_url() -> str:
40
  return settings.OLLAMA_URL
41
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def verify() -> str:
44
  try:
45
  r = requests.get(f"{_ollama_url()}/api/version", timeout=2)
@@ -67,6 +167,10 @@ def start_ollama() -> str:
67
  if ensure_ollama_running():
68
  return "✅ Ollama ya está en ejecución."
69
 
 
 
 
 
70
  # Intento con systemctl (común en Linux)
71
  try:
72
  subprocess.run(
@@ -81,14 +185,19 @@ def start_ollama() -> str:
81
 
82
  # Fallback a 'ollama serve' en segundo plano
83
  try:
 
 
 
84
  subprocess.Popen(
85
- ["ollama", "serve"],
86
  stdout=subprocess.DEVNULL,
87
  stderr=subprocess.DEVNULL,
 
88
  )
89
- time.sleep(2) # Darle tiempo para que inicie
90
- if ensure_ollama_running():
91
- return verify()
 
92
  except FileNotFoundError:
93
  return "❌ No se encontró el binario de Ollama. Instálalo primero."
94
  except Exception as e:
@@ -268,4 +377,4 @@ def ask_ollama_stream(
268
  except requests.exceptions.RequestException as e:
269
  yield f"\n\n⚠️ Error de red: {e}"
270
  except Exception as e:
271
- yield f"\n\n⚠️ Error de conexión: {e}"
 
1
  import json
2
  import hashlib
3
+ import os
4
+ import platform
5
+ import shutil
6
  import subprocess
7
+ import tarfile
8
+ import tempfile
9
  import time
10
  from collections import OrderedDict
11
+ from pathlib import Path
12
  from typing import Dict, Optional, List, Tuple, Generator, Any
13
 
14
  import requests
 
41
 
42
  response_cache = LRUCache(settings.CACHE_MAX_ITEMS)
43
 
44
+ EMBEDDED_OLLAMA_DIR = Path.home() / ".local" / "ollama-lite"
45
+ EMBEDDED_BIN = EMBEDDED_OLLAMA_DIR / "bin" / "ollama"
46
+ EMBEDDED_LIB_DIR = EMBEDDED_OLLAMA_DIR / "lib" / "ollama"
47
+ _OLLAMA_BIN_CACHE: Optional[str] = None
48
+
49
 
50
  def _ollama_url() -> str:
51
  return settings.OLLAMA_URL
52
 
53
 
54
+ def _arch_slug() -> Optional[str]:
55
+ machine = platform.machine().lower()
56
+ if machine in ("x86_64", "amd64"):
57
+ return "amd64"
58
+ if machine in ("arm64", "aarch64"):
59
+ return "arm64"
60
+ return None
61
+
62
+
63
+ def _ensure_embedded_ollama() -> Optional[str]:
64
+ """
65
+ Descarga un binario portátil de Ollama si no existe y devuelve la ruta al ejecutable.
66
+ """
67
+ global _OLLAMA_BIN_CACHE
68
+ if _OLLAMA_BIN_CACHE:
69
+ return _OLLAMA_BIN_CACHE
70
+
71
+ existing = shutil.which("ollama")
72
+ if existing:
73
+ _OLLAMA_BIN_CACHE = existing
74
+ return existing
75
+
76
+ if EMBEDDED_BIN.exists():
77
+ EMBEDDED_BIN.chmod(0o755)
78
+ _OLLAMA_BIN_CACHE = str(EMBEDDED_BIN)
79
+ os.environ.setdefault("PATH", "")
80
+ os.environ["PATH"] = f"{EMBEDDED_BIN.parent}:{os.environ['PATH']}"
81
+ _inject_ld_library_path()
82
+ return _OLLAMA_BIN_CACHE
83
+
84
+ arch = _arch_slug()
85
+ if not arch:
86
+ return None
87
+
88
+ EMBEDDED_OLLAMA_DIR.mkdir(parents=True, exist_ok=True)
89
+ bundle_url = f"https://github.com/ollama/ollama/releases/latest/download/ollama-linux-{arch}.tgz"
90
+ tmp_fd, tmp_path = tempfile.mkstemp(prefix="ollama_bundle_", suffix=".tgz")
91
+ os.close(tmp_fd)
92
+ try:
93
+ print(f"📥 Descargando Ollama portátil ({arch})...")
94
+ with requests.get(bundle_url, stream=True, timeout=(30, 120)) as resp:
95
+ resp.raise_for_status()
96
+ with open(tmp_path, "wb") as bundle:
97
+ for chunk in resp.iter_content(chunk_size=1024 * 1024):
98
+ if chunk:
99
+ bundle.write(chunk)
100
+
101
+ print("📦 Extrayendo Ollama portátil...")
102
+ with tarfile.open(tmp_path, mode="r:gz") as tar:
103
+ members = [
104
+ m
105
+ for m in tar.getmembers()
106
+ if m.name.startswith("bin/") or m.name.startswith("lib/")
107
+ ]
108
+ tar.extractall(path=EMBEDDED_OLLAMA_DIR, members=members)
109
+
110
+ # Elimina librerías CUDA para ahorrar espacio en entornos CPU
111
+ if EMBEDDED_LIB_DIR.exists():
112
+ for cuda_dir in EMBEDDED_LIB_DIR.glob("cuda_*"):
113
+ shutil.rmtree(cuda_dir, ignore_errors=True)
114
+
115
+ EMBEDDED_BIN.chmod(0o755)
116
+ _OLLAMA_BIN_CACHE = str(EMBEDDED_BIN)
117
+ os.environ["PATH"] = f"{EMBEDDED_BIN.parent}:{os.environ.get('PATH', '')}"
118
+ _inject_ld_library_path()
119
+ return _OLLAMA_BIN_CACHE
120
+ except Exception as exc:
121
+ print(f"❌ No se pudo instalar Ollama portátil: {exc}")
122
+ return None
123
+ finally:
124
+ try:
125
+ os.remove(tmp_path)
126
+ except OSError:
127
+ pass
128
+
129
+
130
+ def _inject_ld_library_path():
131
+ current = os.environ.get("LD_LIBRARY_PATH", "")
132
+ lib_path = str(EMBEDDED_LIB_DIR)
133
+ if lib_path not in current.split(":"):
134
+ prefix = f"{lib_path}:" if current else lib_path
135
+ os.environ["LD_LIBRARY_PATH"] = f"{prefix}{current}"
136
+
137
+
138
+ def _ollama_command() -> Optional[str]:
139
+ cmd = _ensure_embedded_ollama()
140
+ return cmd
141
+
142
+
143
  def verify() -> str:
144
  try:
145
  r = requests.get(f"{_ollama_url()}/api/version", timeout=2)
 
167
  if ensure_ollama_running():
168
  return "✅ Ollama ya está en ejecución."
169
 
170
+ ollama_cmd = _ollama_command()
171
+ if not ollama_cmd:
172
+ return "❌ No se encontró el binario de Ollama y no se pudo descargar automáticamente."
173
+
174
  # Intento con systemctl (común en Linux)
175
  try:
176
  subprocess.run(
 
185
 
186
  # Fallback a 'ollama serve' en segundo plano
187
  try:
188
+ env = os.environ.copy()
189
+ _inject_ld_library_path()
190
+ env["LD_LIBRARY_PATH"] = os.environ.get("LD_LIBRARY_PATH", "")
191
  subprocess.Popen(
192
+ [ollama_cmd, "serve"],
193
  stdout=subprocess.DEVNULL,
194
  stderr=subprocess.DEVNULL,
195
+ env=env,
196
  )
197
+ for _ in range(60):
198
+ if ensure_ollama_running():
199
+ return verify()
200
+ time.sleep(1)
201
  except FileNotFoundError:
202
  return "❌ No se encontró el binario de Ollama. Instálalo primero."
203
  except Exception as e:
 
377
  except requests.exceptions.RequestException as e:
378
  yield f"\n\n⚠️ Error de red: {e}"
379
  except Exception as e:
380
+ yield f"\n\n⚠️ Error de conexión: {e}"