Las flow tables ya coinciden
Las flow tables tma1_token_usage_1m, cost_1m, latency_1m y status_1m de TMA1 derivan de span_attributes.gen_ai.*, que este plugin completa por convención. No hay nada más que configurar.
DSH OTel
Sin collector. Sin sidecar. Sin forkear DSH. Se instala como un plugin común de DeepSeek Harness, y cada turno, llamada al modelo y ejecución de herramienta pasa a ser una fila consultable en GreptimeDB.
INSTALACIÓN
dsh plugin --profile headless add @tma1-ai/dsh-plugin-greptimedb
El paquete trae un bundle patch, así que ese único comando lo conecta al profile. dsh plugin delega en el pnpm que esté en tu PATH, y un directorio de profile de dsh es su propio workspace root de pnpm — pnpm 9 se niega a instalar ahí e ignora la configuración de linker que escribe dsh, así que usá pnpm 10 o superior. Los valores por defecto ya apuntan a un GreptimeDB local.
Los spans de chat y los de herramienta comparten trace y dsh.step, así que correlacionarlos es un join de SQL común. Cada timestamp viene del evento de sesión al que pertenece, no de leer el reloj mientras se procesa el evento.
Traces, métricas y logs. signals acepta cualquier subconjunto — una señal desactivada no construye exporter alguno.
El valor por defecto content: none exporta solo estructura y contabilidad. Sin prompts, sin mensajes, sin argumentos de herramientas, sin resultados.
Una configuración inválida falla cuando el plugin carga, nombrando el campo culpable — no en silencio durante la primera exportación.
Señales
Traces para la forma, métricas para retención larga y percentiles a prueba de sampleo, logs para los eventos de sesión crudos.
Los spans de turno son raíces. Los de chat y herramienta cuelgan de ellos como hermanos, correlacionados por dsh.step.
Cuatro caminos cierran un span de chat, incluido el de caída. Ninguno deja un span colgado en un instante arbitrario.
| Situación | Fin y estado |
|---|---|
| El modelo respondió | assistant/message · OK |
| Stream interrumpido | assistant/message · OK, más dsh.response.interrupted |
| La petición falló | el step/end de ese step · ERROR, con el tipo de error |
| Sin evento de cierre (caída, apagado) | último evento visto · UNSET, más dsh.span.unclosed |
Los conteos de DSH son disjuntos: inputTokens es solo entrada sin caché, y las lecturas y escrituras de caché son campos aparte. gen_ai.usage.input_tokens es el total facturado, así que el plugin los suma. La salida incluye los tokens de razonamiento.
Vienen en grafana/ junto a un stack de compose que levanta GreptimeDB y Grafana a la vez. Cada consulta de panel se verifica contra una base real con node grafana/verify.mjs.

Cada tabla enlaza hacia adelante: un trace id abre el waterfall de ese turno, un session id salta entre la vista de traces y la de logs.

Cuatro atributos se vuelven columnas reales vía X-Greptime-Log-Extract-Keys. assistant/chunk nunca se exporta — el assistant/message ya ensamblado trae el mismo contenido.
Métricas
La misma actividad que los traces, vista con PromQL — para retención más larga y percentiles que sobreviven al sampleo.
| Instrumento | Tipo | Dimensiones |
|---|---|---|
gen_ai.client.token.usage | Histogram | gen_ai.token.type (solo input/output), model, provider |
gen_ai.client.operation.duration | Histogram | gen_ai.operation.name, model, provider |
gen_ai.invoke_agent.duration | Histogram | gen_ai.operation.name |
gen_ai.execute_tool.duration | Histogram | gen_ai.operation.name, gen_ai.tool.name |
dsh.token.detail | Histogram | dsh.token.detail_kind (cache_read/cache_write/reasoning) |
dsh.tool.invocations | Counter | gen_ai.tool.name, dsh.tool.outcome |
dsh.turns / dsh.steps | Counter | — |
Configuración
Un patch de profile reemplaza todo el config de esa fila en vez de fusionarse con él, así que reescribí cada campo que quieras conservar.
| Clave | Por defecto | Notas |
|---|---|---|
endpoint | requerido | URL base de OTLP. El plugin agrega el sufijo /v1/{traces,metrics,logs} de cada señal; una ruta por señal se rechaza al cargar. |
database | public | Se envía como X-Greptime-DB-Name. |
username / password | ninguno | Basic auth. Los dos o ninguno. |
signals | las tres | Cualquier subconjunto de traces, metrics, logs. |
content | none | Cuánto payload puede salir del proceso. |
ttl | 180d | Retención de las tablas de logs y traces que crea el plugin, enviada como x-greptime-hints. También acepta forever. Una tabla ya existente conserva la suya hasta un ALTER TABLE. |
Batching, timeouts, nombre de servicio y overrides de tabla tienen valores razonables; la tabla completa está en el README.
Qué sale de la máquina
contentEl valor por defecto retiene todos los payloads. Subilo a propósito, por profile.
| Modo | Se exporta |
|---|---|
none (por defecto) | Estructura y contabilidad: tipos de evento, números de turno y step, conteos de tokens, nombres de herramientas, duraciones, resultados, name y code del error. |
full | Suma el contenido de los mensajes de usuario y asistente, argumentos de herramientas y resultados. |
full+prompt | Suma request/header: el system prompt completo y el schema de cada herramienta. |
Tres cosas no salen en ningún modo: el payload privado meta de una herramienta, el error.message interno de un turno fallido, y el mensaje y stack de una petición fallida. La proyección es una allowlist positiva, así que un tipo de evento que el plugin no conoce — incluido uno que declare un futuro plugin de DSH — exporta su identidad y nada más.
Con TMA1
TMA1 hace de proxy OTLP hacia un GreptimeDB que él mismo administra. Cambiás una línea y DSH aparece en su vista OTel GenAI.
Las flow tables tma1_token_usage_1m, cost_1m, latency_1m y status_1m de TMA1 derivan de span_attributes.gen_ai.*, que este plugin completa por convención. No hay nada más que configurar.
Limitaciones conocidas
Renombra y reempaqueta libremente hasta su primer tag. El rango de peer es la versión exacta contra la que corre CI (0.1.1-rc.2); una nueva release de DSH necesita un bump probado acá.
Los nombres vienen de @opentelemetry/semantic-conventions/incubating y se mueven con él. Los spans llevan tanto gen_ai.provider.name como el obsoleto gen_ai.system.
Las métricas caen en el metric engine, donde la retención es una propiedad de la tabla física. El hint llega a la tabla lógica, que lo guarda y lo muestra pero nunca lo aplica (greptimedb#8951). Ponelo vos con ALTER TABLE greptime_physical_table SET 'ttl' = '180d'.
No hay flush por turno — la exportación sigue la cadencia de los batch processors. Los registros en vuelo cuando expira shutdownTimeoutMillis pueden perderse al salir.
No se cosen dentro del trace padre.