Spaces:
Running
Running
github-actions[bot] commited on
Commit ·
9a1014e
0
Parent(s):
Sync GitHub snapshot to Hugging Face
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .dockerignore +13 -0
- .gitattributes +14 -0
- .gitignore +12 -0
- Dockerfile +35 -0
- README.md +88 -0
- assemble_chroma.py +47 -0
- chat_history.py +113 -0
- chroma_package/chroma.tar.part000 +3 -0
- chroma_package/chroma.tar.part001 +3 -0
- chroma_package/chroma.tar.part002 +3 -0
- chroma_package/chroma.tar.part003 +3 -0
- chroma_package/chroma.tar.part004 +3 -0
- chroma_package/chroma.tar.part005 +3 -0
- chroma_package/chroma.tar.part006 +3 -0
- chroma_package/chroma.tar.part007 +3 -0
- chroma_package/chroma.tar.part008 +3 -0
- chroma_package/chroma.tar.part009 +3 -0
- chroma_package/chroma.tar.part010 +3 -0
- chroma_package/chroma.tar.part011 +3 -0
- chroma_package/chroma.tar.part012 +3 -0
- chroma_package/chroma.tar.part013 +3 -0
- chroma_package/chroma.tar.part014 +3 -0
- chroma_package/chroma.tar.part015 +3 -0
- chroma_package/chroma.tar.part016 +3 -0
- chroma_package/chroma.tar.part017 +3 -0
- chroma_package/chroma.tar.part018 +3 -0
- chroma_package/chroma.tar.part019 +3 -0
- chroma_package/chroma.tar.part020 +3 -0
- chroma_package/chroma.tar.part021 +3 -0
- chroma_package/chroma.tar.part022 +3 -0
- chroma_package/chroma.tar.part023 +3 -0
- chroma_package/chroma.tar.part024 +3 -0
- chroma_package/chroma.tar.part025 +3 -0
- chroma_package/chroma.tar.part026 +3 -0
- chroma_package/chroma.tar.part027 +3 -0
- chroma_package/chroma.tar.part028 +3 -0
- chroma_package/chroma.tar.part029 +3 -0
- chroma_package/chroma.tar.part030 +3 -0
- chroma_package/chroma.tar.part031 +3 -0
- chroma_package/chroma.tar.part032 +3 -0
- chroma_package/chroma.tar.part033 +3 -0
- chroma_package/chroma.tar.part034 +3 -0
- chroma_package/chroma.tar.part035 +3 -0
- chroma_package/chroma.tar.part036 +3 -0
- chroma_package/chroma.tar.part037 +3 -0
- chroma_package/chroma.tar.part038 +3 -0
- chroma_package/chroma.tar.part039 +3 -0
- chroma_package/chroma.tar.part040 +3 -0
- chroma_package/chroma.tar.part041 +3 -0
- chroma_package/chroma.tar.part042 +3 -0
.dockerignore
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.git
|
| 2 |
+
.github
|
| 3 |
+
.agents
|
| 4 |
+
.codex
|
| 5 |
+
.env
|
| 6 |
+
.venv
|
| 7 |
+
venv
|
| 8 |
+
__pycache__
|
| 9 |
+
*.pyc
|
| 10 |
+
chroma_db_docs
|
| 11 |
+
chroma_db_sql
|
| 12 |
+
chroma_db_sql_backup_*
|
| 13 |
+
MODELS
|
.gitattributes
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.gif filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.webp filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.pdf filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.sqlite3 filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
# Hugging Face's GitHub sync may omit LFS-only directories. Keep the packaged
|
| 12 |
+
# Chroma store as regular Git blobs so it is always present in the Docker build.
|
| 13 |
+
chroma_db_docs/** -filter -diff -merge -text
|
| 14 |
+
chroma_package/** filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.keras
|
| 2 |
+
__pycache__/
|
| 3 |
+
.venv/
|
| 4 |
+
chroma_db_sql
|
| 5 |
+
chroma_db_sql_backup_*
|
| 6 |
+
chroma_db_docs/
|
| 7 |
+
venv
|
| 8 |
+
.venv/
|
| 9 |
+
.env
|
| 10 |
+
MODELS/
|
| 11 |
+
chroma_db_sql_backup_20260710_211319
|
| 12 |
+
.jpg
|
Dockerfile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
# Crea y asigna un usuario para HF Spaces (recomendado)
|
| 4 |
+
RUN useradd -m -u 1000 user
|
| 5 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 6 |
+
ENV PYTHONUNBUFFERED=1
|
| 7 |
+
ENV PORT=7860
|
| 8 |
+
ENV ANONYMIZED_TELEMETRY=False
|
| 9 |
+
ENV LOCAL_MODEL_CONTEXT_SIZE=2048
|
| 10 |
+
ENV LOCAL_MODEL_MAX_NEW_TOKENS=256
|
| 11 |
+
ENV LOCAL_MODEL_THREADS=2
|
| 12 |
+
ENV LOCAL_MODEL_BATCH_THREADS=2
|
| 13 |
+
ENV LOCAL_MODEL_BATCH_SIZE=256
|
| 14 |
+
|
| 15 |
+
WORKDIR /home/user/app
|
| 16 |
+
RUN chown -R user:user /home/user/app
|
| 17 |
+
|
| 18 |
+
USER user
|
| 19 |
+
|
| 20 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 21 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 22 |
+
|
| 23 |
+
COPY --chown=user . /home/user/app
|
| 24 |
+
RUN python download_model.py \
|
| 25 |
+
--download-only \
|
| 26 |
+
--repo AlbertiTechnology/qwen3-4b-instruct-gguf \
|
| 27 |
+
--local-dir MODELS/qwen3-4b-instruct-gguf
|
| 28 |
+
RUN python index_documents_to_chroma.py \
|
| 29 |
+
--documents-dir documentos/ASTM \
|
| 30 |
+
--persist-dir chroma_db_docs \
|
| 31 |
+
--collection document_context \
|
| 32 |
+
--reset-collection
|
| 33 |
+
|
| 34 |
+
# Valida la base Chroma generada durante el build y luego inicia la API.
|
| 35 |
+
CMD ["python", "-u", "start_hf.py"]
|
README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Agent API
|
| 3 |
+
emoji: 🤖
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Modelos principales
|
| 11 |
+
ollama pull llama3.2:3b
|
| 12 |
+
ollama pull llava:7b
|
| 13 |
+
ollama pull nomic-embed-text
|
| 14 |
+
|
| 15 |
+
## Modelo Qwen3-4B-Instruct GGUF local
|
| 16 |
+
|
| 17 |
+
Al construir la imagen, Docker descarga el GGUF desde
|
| 18 |
+
`AlbertiTechnology/qwen3-4b-instruct-gguf` y deja una copia local dentro de la
|
| 19 |
+
imagen. Docker ejecuta automaticamente:
|
| 20 |
+
|
| 21 |
+
```powershell
|
| 22 |
+
python download_model.py --download-only --repo AlbertiTechnology/qwen3-4b-instruct-gguf --local-dir MODELS/qwen3-4b-instruct-gguf
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
El modelo queda almacenado en
|
| 26 |
+
`/home/user/app/MODELS/qwen3-4b-instruct-gguf/model.gguf` dentro de la imagen y
|
| 27 |
+
la app lo ejecuta localmente mediante llama.cpp.
|
| 28 |
+
|
| 29 |
+
Esta descarga no requiere un token durante el build porque el repositorio es
|
| 30 |
+
publico.
|
| 31 |
+
|
| 32 |
+
## Contexto documental (Chroma)
|
| 33 |
+
|
| 34 |
+
El agente consulta la coleccion `document_context` de `chroma_db_docs`. Durante
|
| 35 |
+
el build de Docker, la base se reconstruye automaticamente usando solamente los
|
| 36 |
+
PDF y archivos de metadata ubicados en `documentos/ASTM`.
|
| 37 |
+
|
| 38 |
+
Para reconstruirla manualmente fuera de Docker, ejecute:
|
| 39 |
+
|
| 40 |
+
```powershell
|
| 41 |
+
venv\Scripts\python.exe index_documents_to_chroma.py --documents-dir "documentos/ASTM" --reset-collection
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
## Despliegue en Hugging Face Spaces
|
| 45 |
+
|
| 46 |
+
El contenedor ejecuta automaticamente `start_hf.py`. En cada arranque:
|
| 47 |
+
|
| 48 |
+
1. valida `DATABASE_URL`;
|
| 49 |
+
2. valida el modelo local `MODELS/qwen3-4b-instruct-gguf/model.gguf`;
|
| 50 |
+
3. valida la base Chroma y comprueba que `document_context` no este vacia;
|
| 51 |
+
4. inicia FastAPI/Uvicorn en el puerto `7860`.
|
| 52 |
+
|
| 53 |
+
Los reinicios del contenedor reutilizan la base creada dentro de la imagen. Cada
|
| 54 |
+
nuevo build vuelve a generarla desde `documentos/ASTM`.
|
| 55 |
+
|
| 56 |
+
Configure en **Settings > Variables and secrets** del Space:
|
| 57 |
+
|
| 58 |
+
- `DATABASE_URL`: conexion PostgreSQL usada para conversaciones y mensajes.
|
| 59 |
+
|
| 60 |
+
Los logs del Space muestran el proceso completo sin imprimir secrets:
|
| 61 |
+
|
| 62 |
+
```text
|
| 63 |
+
[2026-07-10T12:00:00Z] [BOOT:START] Starting Hugging Face Space initialization.
|
| 64 |
+
[2026-07-10T12:00:00Z] [BOOT:ENV] Required secrets are present (values are not printed).
|
| 65 |
+
[2026-07-10T12:00:00Z] [BOOT:CHROMA] Using build-generated document_context collection (119 chunks).
|
| 66 |
+
[2026-07-10T12:00:00Z] [BOOT:MODEL] Local Qwen model is available at /home/user/app/MODELS/qwen3-4b-instruct-gguf.
|
| 67 |
+
[2026-07-10T12:01:00Z] [BOOT:API] Starting Agent API on 0.0.0.0:7860...
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
Si falta un secret o la base Chroma empaquetada no es valida, el despliegue se
|
| 71 |
+
detiene con un mensaje `[BOOT:ERROR]` visible en **Logs**. La API no arranca con
|
| 72 |
+
una base incompleta.
|
| 73 |
+
|
| 74 |
+
## Lanzar el chat del agente
|
| 75 |
+
|
| 76 |
+
Inicie la API desde la raiz del proyecto:
|
| 77 |
+
|
| 78 |
+
```powershell
|
| 79 |
+
venv\Scripts\python.exe -m uvicorn main:app --host 0.0.0.0 --port 8011 --reload
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
Abra el frontend de chat en `http://127.0.0.1:8011/` (o en
|
| 83 |
+
`http://127.0.0.1:8011/ui`). Se usa el puerto 8011 porque en este entorno los
|
| 84 |
+
puertos 8000 y 8010 ya estan ocupados por otras instancias.
|
| 85 |
+
|
| 86 |
+
La API queda disponible mediante `POST http://127.0.0.1:8011/chat` y por
|
| 87 |
+
WebSocket en `ws://127.0.0.1:8011/ws/chat`. La documentacion interactiva se
|
| 88 |
+
abre en `http://127.0.0.1:8011/docs`.
|
assemble_chroma.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import hashlib
|
| 4 |
+
import tarfile
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
ROOT = Path(__file__).resolve().parent
|
| 9 |
+
CHROMA_DIR = ROOT / "chroma_db_docs"
|
| 10 |
+
PARTS_DIR = ROOT / "chroma_package"
|
| 11 |
+
ARCHIVE_PATH = ROOT / "chroma_db_docs.tar"
|
| 12 |
+
DATABASE_PATH = CHROMA_DIR / "chroma.sqlite3"
|
| 13 |
+
EXPECTED_ARCHIVE_SHA256 = "656a7155238ba2b6ec99baad3fc22698a4765696ba5cad613a6469ab8dba8add"
|
| 14 |
+
EXPECTED_DATABASE_SHA256 = "55b525aca0298422c27393485f9feb146ceb2119fb60f382c81ec44e2b670db6"
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def main() -> None:
|
| 18 |
+
parts = sorted(PARTS_DIR.glob("chroma.tar.part*"))
|
| 19 |
+
if not parts:
|
| 20 |
+
raise FileNotFoundError(f"No packaged Chroma archive parts found in {PARTS_DIR}")
|
| 21 |
+
|
| 22 |
+
digest = hashlib.sha256()
|
| 23 |
+
with ARCHIVE_PATH.open("wb") as output:
|
| 24 |
+
for part in parts:
|
| 25 |
+
payload = part.read_bytes()
|
| 26 |
+
output.write(payload)
|
| 27 |
+
digest.update(payload)
|
| 28 |
+
|
| 29 |
+
actual_sha256 = digest.hexdigest()
|
| 30 |
+
if actual_sha256 != EXPECTED_ARCHIVE_SHA256:
|
| 31 |
+
ARCHIVE_PATH.unlink(missing_ok=True)
|
| 32 |
+
raise RuntimeError(
|
| 33 |
+
f"Invalid assembled Chroma archive: expected {EXPECTED_ARCHIVE_SHA256}, got {actual_sha256}"
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
with tarfile.open(ARCHIVE_PATH) as archive:
|
| 37 |
+
archive.extractall(ROOT, filter="data")
|
| 38 |
+
ARCHIVE_PATH.unlink()
|
| 39 |
+
|
| 40 |
+
database_sha256 = hashlib.sha256(DATABASE_PATH.read_bytes()).hexdigest()
|
| 41 |
+
if database_sha256 != EXPECTED_DATABASE_SHA256:
|
| 42 |
+
raise RuntimeError(f"Invalid extracted Chroma database: {database_sha256}")
|
| 43 |
+
print(f"Assembled and extracted packaged Chroma database from {len(parts)} parts.")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
if __name__ == "__main__":
|
| 47 |
+
main()
|
chat_history.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
from dotenv import load_dotenv
|
| 7 |
+
from sqlalchemy import create_engine, text
|
| 8 |
+
from sqlalchemy.engine import Engine
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
load_dotenv()
|
| 12 |
+
|
| 13 |
+
_engine: Engine | None = None
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def get_engine() -> Engine:
|
| 17 |
+
global _engine
|
| 18 |
+
if _engine is None:
|
| 19 |
+
database_url = os.getenv("DATABASE_URL", "").strip("'\"")
|
| 20 |
+
if not database_url:
|
| 21 |
+
raise RuntimeError("DATABASE_URL is missing in .env")
|
| 22 |
+
_engine = create_engine(database_url, pool_pre_ping=True)
|
| 23 |
+
return _engine
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def list_users() -> list[dict]:
|
| 27 |
+
query = text("SELECT id, username FROM auth_user ORDER BY username")
|
| 28 |
+
with get_engine().connect() as connection:
|
| 29 |
+
return [dict(row) for row in connection.execute(query).mappings()]
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def list_conversations(user_id: int) -> list[dict]:
|
| 33 |
+
query = text("""
|
| 34 |
+
SELECT id, title, created_at, updated_at
|
| 35 |
+
FROM chat_conversation
|
| 36 |
+
WHERE user_id = :user_id
|
| 37 |
+
ORDER BY updated_at DESC
|
| 38 |
+
""")
|
| 39 |
+
with get_engine().connect() as connection:
|
| 40 |
+
return [dict(row) for row in connection.execute(query, {"user_id": user_id}).mappings()]
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def create_conversation(user_id: int, title: str) -> dict:
|
| 44 |
+
query = text("""
|
| 45 |
+
INSERT INTO chat_conversation (user_id, title, created_at, updated_at)
|
| 46 |
+
VALUES (:user_id, :title, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
| 47 |
+
RETURNING id, title, created_at, updated_at
|
| 48 |
+
""")
|
| 49 |
+
with get_engine().begin() as connection:
|
| 50 |
+
user_exists = connection.execute(
|
| 51 |
+
text("SELECT 1 FROM auth_user WHERE id = :user_id"),
|
| 52 |
+
{"user_id": user_id},
|
| 53 |
+
).scalar_one_or_none()
|
| 54 |
+
if not user_exists:
|
| 55 |
+
raise ValueError("User does not exist")
|
| 56 |
+
return dict(connection.execute(query, {"user_id": user_id, "title": title[:255]}).mappings().one())
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def ensure_conversation_owner(conversation_id: int, user_id: int) -> None:
|
| 60 |
+
query = text("""
|
| 61 |
+
SELECT 1 FROM chat_conversation
|
| 62 |
+
WHERE id = :conversation_id AND user_id = :user_id
|
| 63 |
+
""")
|
| 64 |
+
with get_engine().connect() as connection:
|
| 65 |
+
if connection.execute(query, {"conversation_id": conversation_id, "user_id": user_id}).scalar_one_or_none() is None:
|
| 66 |
+
raise ValueError("Conversation does not exist for this user")
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def list_messages(conversation_id: int, user_id: int, limit: int = 100) -> list[dict]:
|
| 70 |
+
ensure_conversation_owner(conversation_id, user_id)
|
| 71 |
+
query = text("""
|
| 72 |
+
SELECT id, role, content, metadata, created_at
|
| 73 |
+
FROM (
|
| 74 |
+
SELECT id, role, content, metadata, created_at
|
| 75 |
+
FROM chat_message
|
| 76 |
+
WHERE conversation_id = :conversation_id
|
| 77 |
+
ORDER BY created_at DESC, id DESC
|
| 78 |
+
LIMIT :limit
|
| 79 |
+
) recent
|
| 80 |
+
ORDER BY created_at, id
|
| 81 |
+
""")
|
| 82 |
+
with get_engine().connect() as connection:
|
| 83 |
+
return [dict(row) for row in connection.execute(
|
| 84 |
+
query,
|
| 85 |
+
{"conversation_id": conversation_id, "limit": limit},
|
| 86 |
+
).mappings()]
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def add_message(conversation_id: int, role: str, content: str, metadata: dict | None = None) -> dict:
|
| 90 |
+
if role not in {"user", "assistant", "system", "tool"}:
|
| 91 |
+
raise ValueError("Invalid message role")
|
| 92 |
+
query = text("""
|
| 93 |
+
INSERT INTO chat_message (conversation_id, role, content, metadata, created_at)
|
| 94 |
+
VALUES (:conversation_id, :role, :content, CAST(:metadata AS jsonb), CURRENT_TIMESTAMP)
|
| 95 |
+
RETURNING id, role, content, metadata, created_at
|
| 96 |
+
""")
|
| 97 |
+
with get_engine().begin() as connection:
|
| 98 |
+
result = connection.execute(query, {
|
| 99 |
+
"conversation_id": conversation_id,
|
| 100 |
+
"role": role,
|
| 101 |
+
"content": content,
|
| 102 |
+
"metadata": json.dumps(metadata or {}),
|
| 103 |
+
}).mappings().one()
|
| 104 |
+
connection.execute(
|
| 105 |
+
text("UPDATE chat_conversation SET updated_at = CURRENT_TIMESTAMP WHERE id = :id"),
|
| 106 |
+
{"id": conversation_id},
|
| 107 |
+
)
|
| 108 |
+
return dict(result)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def format_history(messages: list[dict]) -> str:
|
| 112 |
+
labels = {"user": "Usuario", "assistant": "Asistente", "system": "Sistema", "tool": "Herramienta"}
|
| 113 |
+
return "\n".join(f"{labels.get(item['role'], item['role'])}: {item['content']}" for item in messages)
|
chroma_package/chroma.tar.part000
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:65c35fd47bd4477e6191c8c21089ca964234e3c9828584727900001954967551
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part001
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fa014588b4933dd06d56e4e687a30d7306996b4ec26d36781f28617e32b70646
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part002
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2ba27ec61709167e861a5be3f6201559bae9681dc1486da1d71faa6fd9b419e7
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part003
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fe340cf3d33052d8182c77aeb3348eda508cf3b24d8e3d26062a652e91aa8514
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part004
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:575558a748fe946d0d0739ab89561d6f0dd6b5a4604f7761d0b02922a95bbe47
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part005
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:45176f26eb2b299c40cf3f9299537da87f832850d1d4919e4b64dbc1eb50f4ad
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part006
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0a1a1a9eb15c41f08b17647435808ae0043d9202834328da808aca3291a9f29c
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part007
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e980c7b513a6a215a0e695d608e4e458fb032bda66989ab06a3c7143d7aca38f
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part008
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6e0b9f5a70402f580dc5f3e22d81f3cc2974d8b3097616eb862e90d52a994ba0
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part009
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ca2ba0cae914969c2b40b6968cac201e369c4a491ec6d2bb8a51ad8548e1be1d
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part010
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bb03e8b9b6ca1b7ff3ce1036204813c9329a31ee43629597ba9a012fa056d99a
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part011
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e0c9cfe276a0ec34a38e919797cf0f78ea7ef699f07b8d3369f4bea6edf399d1
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part012
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d15559befdb6feba40ba6f1b1837f89a04fd57d656966e5b6cac80e656f749d2
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part013
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8f07de4d98338f834410f87f9e9b8370dd0a5c5bdc025c4a7a31e15154291741
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part014
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f3511b64d121fb3bf7d153953d337b6ca927e8b5b663ffed966ea2e0c4b4628e
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part015
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3e4f060d4976062708cc284f17f5d7c1abfd96a1a2731906a692ae8e2056f1b0
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part016
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2d31f964daa207c8e01212eec3b65b2b7c0e822230da8050d63071bb48fd8104
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part017
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:452e566c99d6ae83dcc51f76fa6e05ca973b5252b583197c203cd408503edcaa
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part018
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9d520d26d67d9417e106e476412062397d8da26eeaaff990a28d7d8d4a25f9a5
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part019
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6b44242930fedc80d1f296090ec1c6430cf5ee82d6a831fc0c1ccfab556d8839
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part020
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3ced66eb812af87fdd7d4510dec1d961b11305a0f41f62524908bee06039ae7e
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part021
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:98852c1de48470bb0e376fe2ea1c993bf45f1fe7219d7bb9f6160e6cbfd24fe6
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part022
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:669b5a07aab5765ae82aac8c8fc9f1983b59c155da1ccde62fbf1f9b9aca5fda
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part023
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8770292cfbd0bb1f21ac2790430c70bc0f59f9192f7870ee440cc03ef2b71a83
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part024
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8c93c7f96953e1111142a9bc1d4028802b3b23eaca468f1d50dbc7364beeae3c
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part025
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d36867e83340a7c71db5d24dd58d62e18cae88d3fdad220e594f65d51f558028
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part026
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e3f11fe483337ab16ca169371a7ad9073eba0439d81f510bb38da77b2dbc6a66
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part027
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1acff85b6b1c5562abcfc1bfcdfde113af93b93c7e21bb3090c77886265b13b1
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part028
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:caae7161018565b34a3415ba796afc3ce6d416adc88839529bd583b21ddbafc2
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part029
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:aa653f4e877833b34c9c0d4872864dede62a7cb3342fe3e30f0a802f6240806c
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part030
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:34a833b0ddfd963065e6495865615c55551ef1e303e1b7d35e445eaf9f132cf9
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part031
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a6a871bad03781ede5e0b2bf8ac942b863fb0d148f8c543173e112c06c3a13e0
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part032
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4bfab78062f40a1584d3d2e770bcb68bf2fe3dfde4748212b05f1ac0b7479e4c
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part033
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:10a8257a9fe92f51f5ed87073e2b2f8bb9581e5f89b21c0d15a4a430a6faacb2
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part034
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3b43d1c0a5ebc09ee9946e27d946f0b330e29395fa4a28d2854c28c0895edc13
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part035
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4926d441aa6853986161bf90d6f72b4e03e645172c2a3771fd884126991df108
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part036
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f7188e748dd4299c9ffe9a29dfe335f042ceb4a1e013b0136f58f2ee2cf81490
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part037
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0c07f9538a0897a8fc376bde94411119d9d78a488090eba6b899be1099ec2657
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part038
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:792bf9b178553098f3bb8093f62da2745d7df06e8a6fd83cf78d62fe8d5e6b9f
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part039
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7e54b2490d46a2da6c5624b95a41fbe2d3ebd9ca1abc08aae8b394894692fb94
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part040
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ea33273014fb24f70ee5fab9dc7ea9c5d1d6425aaeb6e5f2b7aa653437e10642
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part041
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:727da8b64de123485c3fb80e3deefcbb23561773fe36ca2fd6765d38fe0e70b7
|
| 3 |
+
size 1048576
|
chroma_package/chroma.tar.part042
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:47befd62d954e37220dfcb702e0aee94c78337ce74bee676600a91941fbe2ac7
|
| 3 |
+
size 1048576
|