# Esquema de almacenamiento y consultas directas

Trackio almacena cada proyecto en su propia base de datos SQLite y puede exportar esos datos a parquet para sincronización, Spaces estáticos y análisis directo. Esta página documenta el esquema SQLite en vivo, la estructura parquet derivada y el nuevo comando `trackio query` para ejecutar SQL de solo lectura.

## Dónde se encuentra los datos

- Las bases de datos de proyectos locales se encuentran en `TRACKIO_DIR`, cuyo valor predeterminado es `~/.cache/huggingface/trackio`.
- Cada proyecto se almacena como un archivo SQLite separado: `{project}.db`.
- Los medios y los archivos subidos se encuentran en `TRACKIO_DIR/media/`.
- Cada trazas se escribe además como un archivo de sesión de agente renderizable en Hub bajo `TRACKIO_DIR/traces/` (en un Space, el prefijo `traces/` del bucket). Estos son un artefacto de renderizado para el visor de trazas de agentes de Hub; la tabla `traces` sigue siendo la fuente de autoridad. Consulte [Trazas](./traces).
- Al sincronizar con Hugging Face, Trackio exporta archivos parquet desde la base de datos SQLite antes de la carga.

## Consultar datos directamente

Utilice la CLI cuando necesite una consulta general que no esté cubierta por `trackio list` o `trackio get`:

```sh
trackio query project --project "my-project" --sql "SELECT run_name, MAX(step) AS last_step FROM metrics GROUP BY run_name ORDER BY last_step DESC"
```

Añada `--json` para una salida legible por máquina:

```sh
trackio query project --project "my-project" --sql "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name" --json
```

El mismo comando funciona contra un Space remoto:

```sh
trackio query project --project "my-project" --sql "SELECT COUNT(*) AS num_alerts FROM alerts" --space username/my-space --json
```

`trackio query` solo acepta consultas de solo lectura `SELECT`, `WITH` y consultas seguras de esquema `PRAGMA`, incluida la lectura de `PRAGMA user_version`.

## Esquema de SQLite

El esquema en vivo se define en `trackio/sqlite_storage.py` mediante `SQLiteStorage.init_db()`.

### `metrics`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `timestamp` | `TEXT` | Marca de tiempo ISO |
| `run_name` | `TEXT` | Identificador de ejecución |
| `step` | `INTEGER` | Paso de entrenamiento |
| `metrics` | `TEXT` | Blob JSON que contiene los valores de métricas registrados |
| `log_id` | `TEXT` | Clave de deduplicación opcional agregada por la migración aditiva |
| `space_id` | `TEXT` | Marcador opcional de sincronización pendiente añadido por la migración aditiva |

Índices:

- `idx_metrics_run_step` en `(run_name, step)`
- `idx_metrics_run_timestamp` en `(run_name, timestamp)`
- Índice parcial único `idx_metrics_log_id` sobre `log_id`
- Índice parcial `idx_metrics_pending` sobre `space_id`

### `configs`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `run_name` | `TEXT` | Identificador de ejecución |
| `config` | `TEXT` | Blob JSON que contiene la configuración de la ejecución |
| `created_at` | `TEXT` | Marca de tiempo ISO |

Índices y restricciones:

- `UNIQUE(run_name)`
- `idx_configs_run_name` en `(run_name)`

### `system_metrics`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `timestamp` | `TEXT` | Marca de tiempo ISO |
| `run_name` | `TEXT` | Identificador de ejecución |
| `metrics` | `TEXT` | Blob JSON que contiene métricas del sistema |
| `log_id` | `TEXT` | Clave de deduplicación opcional agregada por la migración aditiva |
| `space_id` | `TEXT` | Marcador opcional de sincronización pendiente añadido por la migración aditiva |

Índices:

- `idx_system_metrics_run_timestamp` en `(run_name, timestamp)`
- Índice parcial único `idx_system_metrics_log_id` sobre `log_id`
- índice parcial `idx_system_metrics_pending` sobre `space_id`

### `traces`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `TEXT` | Clave primaria |
| `run_id` | `TEXT` | Identificador de ejecución estable |
| `timestamp` | `TEXT` | Marca de tiempo ISO cuando se registró la traza |
| `run_name` | `TEXT` | Ejecutar nombre de visualización |
| `step` | `INTEGER` | Paso de entrenamiento o registro |
| `key` | `TEXT` | Clave utilizada en el payload de `trackio.log()` |
| `trace_index` | `INTEGER` | Posición cuando las trazas se registraron como una lista |
| `messages` | `TEXT` | Mensajes de estilo OpenAI en JSON |
| `metadata` | `TEXT` | Metadatos de la traza JSON |
| `search_text` | `TEXT` | Texto aplanado utilizado por la búsqueda de trazas |
| `log_id` | `TEXT` | Clave opcional de deduplicación del registro de origen |
| `space_id` | `TEXT` | Marcador opcional de sincronización pendiente |
| `spans` | `TEXT` | Spans de ejecución en JSON; el valor predeterminado es `[]` |

Índices:

- `idx_traces_run_step` en `(run_id, step)`
- `idx_traces_run_timestamp` en `(run_id, timestamp)`
- `idx_traces_search` en `search_text`

### `project_metadata`

| Columna | Tipo | Notas |
|--------|------|-------|
| `key` | `TEXT` | Clave primaria |
| `value` | `TEXT` | Valor de metadatos |

### `pending_uploads`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `space_id` | `TEXT` | Espacio de destino |
| `run_name` | `TEXT` | Nombre de ejecución opcional |
| `step` | `INTEGER` | Paso opcional |
| `file_path` | `TEXT` | Ruta local absoluta |
| `relative_path` | `TEXT` | Ruta relativa de los medios |
| `created_at` | `TEXT` | Marca de tiempo ISO |

### `alerts`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `timestamp` | `TEXT` | Marca de tiempo ISO |
| `run_name` | `TEXT` | Identificador de ejecución |
| `title` | `TEXT` | Título de la alerta |
| `text` | `TEXT` | Cuerpo de la alerta opcional |
| `level` | `TEXT` | `info`, `warn` o `error` |
| `step` | `INTEGER` | Paso de entrenamiento opcional |
| `alert_id` | `TEXT` | Clave de deduplicación opcional |

Índices:

- `idx_alerts_run` en `(run_name)`
- `idx_alerts_timestamp` en `(timestamp)`
- Índice parcial único `idx_alerts_alert_id` sobre `alert_id`

### `artifacts`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `name` | `TEXT` | Nombre del artefacto |
| `type` | `TEXT` | Categoría libre como `model` o `dataset` |
| `description` | `TEXT` | Descripción opcional |
| `created_at` | `TEXT` | Marca de tiempo ISO |

Índices y restricciones:

- `UNIQUE(name)`

### `artifact_versions`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `artifact_id` | `INTEGER` | Referencias `artifacts.id` |
| `version` | `INTEGER` | Número de versión secuencial que comienza en 0 |
| `manifest_digest` | `TEXT` | Digesto hexadecimal SHA-256 del JSON del manifiesto canónico |
| `manifest` | `TEXT` | Blob JSON que lista las entradas del manifiesto con `path`, `digest`, `size` y `ref` opcional |
| `metadata` | `TEXT` | Metadatos JSON opcionales |
| `size_bytes` | `INTEGER` | Tamaño total de las entradas del manifiesto de la versión (suma de `size`) en bytes |
| `producer_run_id` | `TEXT` | Identificador opcional de la ejecución que lo generó |
| `producer_run_name` | `TEXT` | Nombre opcional de la ejecución que lo produce |
| `created_at` | `TEXT` | Marca de tiempo ISO |

Índices y restricciones:

- `UNIQUE(artifact_id, version)`
- `UNIQUE(artifact_id, manifest_digest)`

### `artifact_aliases`

| Columna | Tipo | Notas |
|--------|------|-------|
| `artifact_id` | `INTEGER` | Referencias `artifacts.id` |
| `alias` | `TEXT` | Nombre de alias como `latest` |
| `artifact_version_id` | `INTEGER` | Referencias `artifact_versions.id` |

Índices y restricciones:

- `PRIMARY KEY (artifact_id, alias)`

### `run_artifact_links`

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `run_id` | `TEXT` | Identificador de ejecución opcional |
| `run_name` | `TEXT` | Nombre de ejecución opcional |
| `artifact_version_id` | `INTEGER` | Referencias `artifact_versions.id` |
| `direction` | `TEXT` | `input` o `output` |
| `created_at` | `TEXT` | Marca de tiempo ISO |

Índices y restricciones:

- `CHECK(direction IN ('input', 'output'))`
- `idx_run_artifact_links_run` en `(run_id, run_name)`
- `idx_run_artifact_links_version` en `(artifact_version_id)`
- Índice único `idx_run_artifact_links_unique` sobre `(run_id, artifact_version_id, direction)`

### Tablas de registro

Las bases de datos de [Registry](./registry), los proyectos denominados `registry-<name>` creados mediante `Api.create_registry`, contienen las tablas estándar anteriores más las cuatro tablas siguientes, definidas en `trackio/registry_storage.py` por `RegistryStorage.init_registry_db()`. Los números de versión de las colecciones provienen de `collections.next_version`, que solo avanza: una versión de colección nunca se reutiliza, incluso después de un unlink. Cada mutación del registro también añade una fila a `registry_events` en la misma transacción.

**`collections`**

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `name` | `TEXT` | Nombre de la colección, `UNIQUE` |
| `type` | `TEXT` | El único tipo de artefacto que acepta la colección |
| `description` | `TEXT` | Descripción opcional |
| `next_version` | `INTEGER` | Contador monótono para los números de versión de la colección |
| `created_at` | `TEXT` | Marca de tiempo ISO |

**`collection_links`**

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `collection_id` | `INTEGER` | Referencias `collections(id)` |
| `collection_version` | `INTEGER` | Número de versión dentro de la colección (`v0`, `v1`, ...) |
| `source_project` | `TEXT` | El proyecto en el que se registró la versión del artefacto vinculado |
| `source_artifact` | `TEXT` | Nombre del artefacto vinculado |
| `source_version` | `INTEGER` | Versión del artefacto vinculado |
| `source_space_id` | `TEXT` | Espacio que contiene la versión de origen, `NULL` cuando es local |
| `source_bucket_id` | `TEXT` | Bucket que contiene la versión de origen, `NULL` cuando es local |
| `created_at` | `TEXT` | Marca de tiempo ISO |

Restricciones:

- `UNIQUE(collection_id, source_project, source_artifact, source_version)` — una versión de origen se vincula como máximo una vez por colección
- `UNIQUE(collection_id, collection_version)`

**`collection_aliases`**

| Columna | Tipo | Notas |
|--------|------|-------|
| `collection_id` | `INTEGER` | Referencias `collections(id)` |
| `alias` | `TEXT` | Nombre de alias |
| `link_id` | `INTEGER` | Referencias `collection_links(id)` |

Restricciones:

- `PRIMARY KEY (collection_id, alias)` — una versión por alias por colección

**`registry_events`**

| Columna | Tipo | Notas |
|--------|------|-------|
| `id` | `INTEGER` | Clave primaria |
| `event_uid` | `TEXT` | `<compact-utc>-<writer>-<seq>`, único; ordena totalmente los eventos entre escritores y hace que la reproducción sea idempotente |
| `ts` | `TEXT` | Marca de tiempo ISO |
| `kind` | `TEXT` | `create`, `link`, `promote`, `update` o `unlink` |
| `payload` | `TEXT` | Blob JSON que describe la mutación |

El registro es la forma autoritativa de un registro: las tablas anteriores son una proyección de este. Un registro respaldado por un bucket almacena los mismos eventos como un objeto cada uno (consulte la guía de Registro) y materializa estas tablas localmente.

## Cómo se almacenan las cargas de métricas

- Las métricas de usuario se almacenan como texto JSON en `metrics.metrics`.
- Las métricas del sistema se almacenan como texto JSON en `system_metrics.metrics`.
- La configuración de ejecución se almacena como texto JSON en `configs.config`.
- Las API de alto nivel de Trackio deserializan estos blobs JSON antes de devolverlos.

Patrón de consulta directa común:

```sh
trackio query project --project "my-project" --sql "SELECT timestamp, step, metrics FROM metrics WHERE run_name = 'run-1' ORDER BY step DESC LIMIT 5"
```

Ejemplo de introspección de esquema:

```sh
trackio query project --project "my-project" --sql "PRAGMA table_info(metrics)"
```

## Distribución de Parquet

Trackio deriva archivos parquet de SQLite aplanando las columnas JSON en columnas regulares.

### Exportaciones locales de parquet

`SQLiteStorage.export_to_parquet()` escribe:

- `{project}.parquet` de `metrics`
- `{project}_system.parquet` de `system_metrics`
- `{project}_configs.parquet` de `configs`
- `{project}_traces.parquet` de `traces`
- `{project}_{table}.parquet` para cada tabla de artefactos (`artifacts`, `artifact_versions`, `artifact_aliases`, `run_artifact_links`)

Los archivos parquet aplanados conservan las columnas estructurales, como `timestamp`, `run_name` y `step`, y luego añaden una columna por cada clave encontrada en la carga JSON.

### Espacio estático y exportaciones de conjuntos de datos

`SQLiteStorage.export_for_static_space()` escribe:

- `metrics.parquet`
- `aux/system_metrics.parquet`
- `aux/configs.parquet`
- `aux/traces.parquet`
- `aux/artifacts.parquet`, `aux/artifact_versions.parquet`, `aux/artifact_aliases.parquet` y `aux/run_artifact_links.parquet`
- `runs.json`
- `settings.json`

La implementación estática copia los archivos multimedia a `media/` y los blobs de artefactos a `artifacts/blobs/sha256/{prefix}/{digest}` junto a estas exportaciones, de modo que el panel de control solo para navegador pueda servir las descargas de artefactos directamente desde el conjunto de datos o el bucket.

Este es el diseño utilizado para los Spaces estáticos y los conjuntos de datos exportados.

## Ejemplos de consultas útiles

Listar tablas:

```sh
trackio query project --project "my-project" --sql "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name"
```

Encuentra el paso más reciente de cada ejecución:

```sh
trackio query project --project "my-project" --sql "SELECT run_name, MAX(step) AS last_step FROM metrics GROUP BY run_name ORDER BY last_step DESC"
```

Inspeccionar alertas recientes:

```sh
trackio query project --project "my-project" --sql "SELECT timestamp, run_name, level, title, step FROM alerts ORDER BY timestamp DESC LIMIT 20"
```

Inspeccionar las configuraciones almacenadas:

```sh
trackio query project --project "my-project" --sql "SELECT run_name, created_at, config FROM configs ORDER BY created_at DESC"
```

## Versiones de esquema y migraciones

Cada base de datos de proyecto registra su versión de esquema en `PRAGMA user_version`:

```sh
trackio query project --project "my-project" --sql "PRAGMA user_version"
```

`SQLiteStorage.init_db()` actualiza la base de datos hasta la `SCHEMA_VERSION` (definida en `trackio/sqlite_storage.py`) cada vez que se abre para escritura, y un servidor de Trackio migra la base de datos de cada proyecto al iniciar. Las bases de datos escritas antes de que existiera el control de versiones informan la versión 0 y se actualizan a la versión 1 mediante las sentencias aditivas `CREATE TABLE IF NOT EXISTS` y `ALTER TABLE ... ADD COLUMN` en `init_db()`. La versión 2 asigna un `run_id` a cada tabla con ámbito de ejecución en las bases de datos escritas antes de Trackio 0.24: esas versiones identificaban una ejecución solo por su nombre, por lo que cada nombre de ejecución se convierte en una ejecución con un nuevo identificador.

Para cambiar el esquema o reescribir datos existentes de una forma que esas sentencias no pueden expresar, añade un paso a `_SCHEMA_MIGRATIONS` e incrementa `SCHEMA_VERSION`:

```python
def _split_loss_column(cursor):
    cursor.execute("ALTER TABLE metrics ADD COLUMN loss REAL")
    cursor.execute("UPDATE metrics SET loss = json_extract(metrics, '$.loss')")

SCHEMA_VERSION = 3
_SCHEMA_MIGRATIONS = {2: _migrate_add_run_ids, 3: _split_loss_column}
```

`_SCHEMA_MIGRATIONS[n]` actualiza una base de datos de la versión `n - 1` a la `n`. Cada paso se ejecuta en su propio savepoint junto con su incremento de versión, por lo que un paso fallido deja la base de datos en la última versión completada. Una base de datos con una versión más reciente que la que conoce la instancia de Trackio en ejecución se deja intacta.

`trackio bump` se basa en esto: tras reiniciar un Space en la versión local de Trackio, comprueba que cada base de datos de proyecto informe de la `SCHEMA_VERSION` local. La prueba e2e en `tests/e2e-spaces/test_space_bump.py` ejecuta cada migración contra los datos escritos por Trackio 0.21.0 y 0.37.1.

## Notas sobre la estabilidad

Trackio aún está en beta. El esquema está documentado aquí para que las personas y los agentes puedan consultarlo directamente, pero las versiones futuras pueden evolucionar el esquema y requerir migraciones o bases de datos locales regeneradas.

