mirror of
https://github.com/FJurmanovic/sugarr.git
synced 2026-11-11 15:51:09 +00:00
Compare commits
10 Commits
a90f9dc19b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c82a8416f | |||
| cb5e7085bf | |||
| 9a4d254968 | |||
| df4ca0bf76 | |||
| 6f0300f038 | |||
| e47c57b50d | |||
| b615f08039 | |||
| a5120d5153 | |||
| 2ffba19392 | |||
| a537f22384 |
@@ -4,7 +4,12 @@ JELLYFIN_API_KEY=
|
|||||||
JELLYFIN_USER_ID=
|
JELLYFIN_USER_ID=
|
||||||
TMDB_API_KEY=
|
TMDB_API_KEY=
|
||||||
|
|
||||||
# LLM (disabled by default — works with Ollama, OpenAI, LM Studio, etc.)
|
# LLM (disabled by default — any OpenAI-compatible endpoint: Ollama, OpenAI,
|
||||||
|
# LM Studio, OpenRouter, etc.)
|
||||||
|
# For OpenRouter specifically: LLM_BASE_URL=https://openrouter.ai/api/v1,
|
||||||
|
# LLM_API_KEY=<your OpenRouter key>, LLM_MODEL=<an OpenRouter model slug>.
|
||||||
|
# Enable "Usage Accounting" on the OpenRouter account to get real per-request
|
||||||
|
# cost reported back to Langfuse instead of a price-list estimate.
|
||||||
LLM_ENABLED=false
|
LLM_ENABLED=false
|
||||||
LLM_BASE_URL=http://localhost:11434/v1
|
LLM_BASE_URL=http://localhost:11434/v1
|
||||||
LLM_API_KEY=
|
LLM_API_KEY=
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
FROM node:22-alpine AS build
|
FROM node:22-alpine AS build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN corepack enable
|
RUN corepack enable
|
||||||
COPY package.json pnpm-lock.yaml ./
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
RUN pnpm install --frozen-lockfile --ignore-scripts
|
RUN pnpm install --frozen-lockfile --ignore-scripts
|
||||||
COPY tsconfig.json ./
|
COPY tsconfig.json ./
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
@@ -11,7 +11,7 @@ FROM node:22-alpine AS runtime
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
RUN corepack enable
|
RUN corepack enable
|
||||||
COPY package.json pnpm-lock.yaml ./
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
RUN pnpm install --frozen-lockfile --prod
|
RUN pnpm install --frozen-lockfile --prod
|
||||||
COPY --from=build /app/dist ./dist
|
COPY --from=build /app/dist ./dist
|
||||||
ENTRYPOINT ["node", "dist/app/index.js"]
|
ENTRYPOINT ["node", "dist/app/index.js"]
|
||||||
|
|||||||
@@ -34,9 +34,8 @@ services:
|
|||||||
LANGFUSE_BASE_URL: ${LANGFUSE_BASE_URL}
|
LANGFUSE_BASE_URL: ${LANGFUSE_BASE_URL}
|
||||||
LANGFUSE_PROMPT_CACHE_ENABLED: ${LANGFUSE_PROMPT_CACHE_ENABLED}
|
LANGFUSE_PROMPT_CACHE_ENABLED: ${LANGFUSE_PROMPT_CACHE_ENABLED}
|
||||||
networks:
|
networks:
|
||||||
- media
|
|
||||||
- coolify
|
- coolify
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
media:
|
|
||||||
coolify:
|
coolify:
|
||||||
|
external: true
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "dist/app/index.js",
|
"main": "dist/app/index.js",
|
||||||
|
"packageManager": "pnpm@11.21.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsx watch src/app/index.ts",
|
"dev": "tsx watch src/app/index.ts",
|
||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
@@ -13,9 +14,12 @@
|
|||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@langfuse/client": "^5.10.0",
|
||||||
|
"@langfuse/otel": "^5.10.0",
|
||||||
|
"@langfuse/tracing": "^5.10.0",
|
||||||
|
"@opentelemetry/sdk-node": "^0.221.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"fastify": "^5.0.0",
|
"fastify": "^5.0.0",
|
||||||
"langfuse": "^3.30.0",
|
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
"openai": "^4.60.0",
|
"openai": "^4.60.0",
|
||||||
"pino": "^9.4.0",
|
"pino": "^9.4.0",
|
||||||
|
|||||||
707
pnpm-lock.yaml
generated
707
pnpm-lock.yaml
generated
@@ -8,15 +8,24 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@langfuse/client':
|
||||||
|
specifier: ^5.10.0
|
||||||
|
version: 5.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@langfuse/otel':
|
||||||
|
specifier: ^5.10.0
|
||||||
|
version: 5.10.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))
|
||||||
|
'@langfuse/tracing':
|
||||||
|
specifier: ^5.10.0
|
||||||
|
version: 5.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-node':
|
||||||
|
specifier: ^0.221.0
|
||||||
|
version: 0.221.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0)
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.4.5
|
specifier: ^16.4.5
|
||||||
version: 16.6.1
|
version: 16.6.1
|
||||||
fastify:
|
fastify:
|
||||||
specifier: ^5.0.0
|
specifier: ^5.0.0
|
||||||
version: 5.11.3
|
version: 5.11.3
|
||||||
langfuse:
|
|
||||||
specifier: ^3.30.0
|
|
||||||
version: 3.38.20
|
|
||||||
node-cron:
|
node-cron:
|
||||||
specifier: ^3.0.3
|
specifier: ^3.0.3
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
@@ -393,6 +402,15 @@ packages:
|
|||||||
'@fastify/proxy-addr@5.1.0':
|
'@fastify/proxy-addr@5.1.0':
|
||||||
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
|
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
|
||||||
|
|
||||||
|
'@grpc/grpc-js@1.14.4':
|
||||||
|
resolution: {integrity: sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==}
|
||||||
|
engines: {node: '>=12.10.0'}
|
||||||
|
|
||||||
|
'@grpc/proto-loader@0.8.1':
|
||||||
|
resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@humanwhocodes/config-array@0.13.0':
|
'@humanwhocodes/config-array@0.13.0':
|
||||||
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
|
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
@@ -409,6 +427,34 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec@1.5.5':
|
'@jridgewell/sourcemap-codec@1.5.5':
|
||||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||||
|
|
||||||
|
'@js-sdsl/ordered-map@4.4.2':
|
||||||
|
resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
|
||||||
|
|
||||||
|
'@langfuse/client@5.10.0':
|
||||||
|
resolution: {integrity: sha512-WI//PooxMyFEFtDy77XNv6WxOpd52U6HNgI8cqhPX+S2aJ8Mz2UTWz7olwzLlrwToJpsy5aZNtpdhsol2Umubw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.9.0
|
||||||
|
|
||||||
|
'@langfuse/core@5.10.0':
|
||||||
|
resolution: {integrity: sha512-XXz1DBEwGMpNXz8DZHFsixqo+3vBkfYpat3oLLldRMuT8ur6OWQ81Kgh5Obh0CZ31adaR4FrLTHh4N9eBXo64A==}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.9.0
|
||||||
|
|
||||||
|
'@langfuse/otel@5.10.0':
|
||||||
|
resolution: {integrity: sha512-q1expgCm3QwjWII0PeCJJSyEV5o96FQ4VNzL5hgziWGmCPCd9TrrWxWxHdSY7cpZWddEJCp71zdh1nq0xISOpQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.9.0
|
||||||
|
'@opentelemetry/core': ^2.0.1
|
||||||
|
'@opentelemetry/exporter-trace-otlp-http': '>=0.202.0 <1.0.0'
|
||||||
|
'@opentelemetry/sdk-trace-base': ^2.0.1
|
||||||
|
|
||||||
|
'@langfuse/tracing@5.10.0':
|
||||||
|
resolution: {integrity: sha512-//S9dcZszmEk2yx+h7eg10mEMswyU7MYLkGIJlO6gUfrXA+3Y5pvNiZNZU3cfDus94OAr16dHQpg3nNFiziZjg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.9.0
|
||||||
|
|
||||||
'@napi-rs/lzma-linux-x64-gnu@1.5.1':
|
'@napi-rs/lzma-linux-x64-gnu@1.5.1':
|
||||||
resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==}
|
resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==}
|
||||||
engines: {node: ^22.20 || ^24.12 || >=25}
|
engines: {node: ^22.20 || ^24.12 || >=25}
|
||||||
@@ -428,9 +474,210 @@ packages:
|
|||||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
'@opentelemetry/api-logs@0.221.0':
|
||||||
|
resolution: {integrity: sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
'@opentelemetry/api@1.9.1':
|
||||||
|
resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
'@opentelemetry/configuration@0.221.0':
|
||||||
|
resolution: {integrity: sha512-uE9y56Zdi9Gt/RdxYnVOo3YmFZkKJJMA0gqtBe8wh8gdtF5Asqe+Oh/TWiDtFb1s+31jNY4CWgnfIB1KOITfFA==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.9.0
|
||||||
|
|
||||||
|
'@opentelemetry/context-async-hooks@2.10.0':
|
||||||
|
resolution: {integrity: sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/core@2.10.0':
|
||||||
|
resolution: {integrity: sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-logs-otlp-grpc@0.221.0':
|
||||||
|
resolution: {integrity: sha512-txG1G0IrYSsKKMeiWZfj/i5cQmWB+h+hf3HzPpF3RqZVwp+iQQEIsv8Vtmzy6RWVdHdJZfygmVrBI39YTBvWcw==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-logs-otlp-http@0.221.0':
|
||||||
|
resolution: {integrity: sha512-nKXkr4Tomi6fjYVOf+ytcW3dZAVr4v4Bv5gsT6dr2gvpUPJpKgHB4XbMufMsPotRE3g0XH2GwVVCkN2w6SON+Q==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-logs-otlp-proto@0.221.0':
|
||||||
|
resolution: {integrity: sha512-AH6EY+47gXFaWYgG3hfeOneGiE9xIZGtDBk+9g0sM8NZWzsQhhmqPbQQXJzS7pyCh5jRRr2nYNXVrkCmoojRvQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-grpc@0.221.0':
|
||||||
|
resolution: {integrity: sha512-KOgCtO15FC6C1T/xOqBcr7EyUs7B+7yomGNb5Y97d3s38rPbCCk5sewkmE2b0/itOkQ/PptX8CLlD+kn2mEtTg==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-http@0.221.0':
|
||||||
|
resolution: {integrity: sha512-sRfCKbOzgy8xZQV2as0RzIZlnCmCseCKZGLfRcrpo2CBngJDr+rPtX0zkG0+oUCV5kfQPUoW3W3C96Ag3Y/Clg==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-proto@0.221.0':
|
||||||
|
resolution: {integrity: sha512-YMF4LveY2I3yhw61rn6nmC9FE8U24IZHPeKU1Duc5+sbwjMd8FwZAwba318ImdThCg/HuVQvhm2y6bfgNPnfYg==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-prometheus@0.221.0':
|
||||||
|
resolution: {integrity: sha512-kW79a20qWESIuAdDrxzg9WKM98twV/NBWBFRAH57ap/+ssZhiCo0hckzKT0zpuwR/gSHrFAQhJL0bYDrnEM34g==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-trace-otlp-grpc@0.221.0':
|
||||||
|
resolution: {integrity: sha512-zXminlZedtq9LvOW64CnNkOqk15zV75k8JgtdTuWFge6+jk2m4GmAUm6L2eIiG1o2a2bZxXw2PDrszm+bps0IA==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-trace-otlp-http@0.221.0':
|
||||||
|
resolution: {integrity: sha512-AySXiKoC+meiWm6zdVj5T2LnPDZuatveBby1cMOeQteIWsYXAUxs8Sru13G2pVSPrUXz6vF+og7QVBX6GdC/oQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-trace-otlp-proto@0.221.0':
|
||||||
|
resolution: {integrity: sha512-Z9i2T7vgZbWe9rSLYxXVIbeW+XyzUq4rZanW3ZyVNwVDqCsh0EJKUgBWWQ0CZfeuUA+RQPzKgJQHMuWAUnKqXw==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-zipkin@2.10.0':
|
||||||
|
resolution: {integrity: sha512-7gsvgf0UDoJ4l9ObrwBmz5G/ZogiPk+lq+g5GpLp24YQF/vPM/BSsnOfcLnfinast5ASUgLo78uSC/ObjlnXgg==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.0.0
|
||||||
|
|
||||||
|
'@opentelemetry/instrumentation@0.221.0':
|
||||||
|
resolution: {integrity: sha512-cCk80Z/iRDf/5gfsKMB4f74LqVA5yKETB/9ojPzVW/6/f70iu89nJvGxsFCxx4XfSohaOofkU19kiYm84AiAlw==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/otlp-exporter-base@0.221.0':
|
||||||
|
resolution: {integrity: sha512-UFPIq80OH3Ns/oPFHRj14d4DTOxUo+MUFU8hUiCq5jTqFhdeJnfVSANHT+xp92409cA+oxzvlZCe6NM1wvCuBA==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/otlp-grpc-exporter-base@0.221.0':
|
||||||
|
resolution: {integrity: sha512-rQDmNgyiGCTrescjnzH2ntVyUKVIq6I2UjuK8+stT/Xg0ZOT71FVJqwjFdspQl6Yol/Yqsut9bDo+ame8oTmDQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/otlp-transformer@0.221.0':
|
||||||
|
resolution: {integrity: sha512-lg6lkOU08Az23jVcn/0Els9HP+V8PnR4Km6p0KgpTggS0n/WuhnmY64rSh83Of9iR9nD+dpWr6adlcX8KzAwjg==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': ^1.3.0
|
||||||
|
|
||||||
|
'@opentelemetry/propagator-b3@2.10.0':
|
||||||
|
resolution: {integrity: sha512-GnA5B24H+1w8BO21J0q+IWNB0z1v+AGbcquTdIt/dufibhnhgxaA8YKvz0I3akRZhB1jHT+/tlzK+qlAjEDybQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/propagator-jaeger@2.10.0':
|
||||||
|
resolution: {integrity: sha512-yw/IX8DL470dSMZJoE82ScfYGp7JWZ/G8kFJo35ZILUVTB2jFPTOaioN+8s09pH0RHsWNhweVZb+ZnjJJpCChg==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/resources@2.10.0':
|
||||||
|
resolution: {integrity: sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.3.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-logs@0.221.0':
|
||||||
|
resolution: {integrity: sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.4.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-metrics@2.10.0':
|
||||||
|
resolution: {integrity: sha512-t6r1VSvXNtSDnPXU1FbZeetJb7yyovHmgu0wRSoftxtE0g2rSNhQZQUy69sRUCL+iioJpX8SN/S6wq6ZtvLySQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.9.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-node@0.221.0':
|
||||||
|
resolution: {integrity: sha512-UbYuvtBrQQB5Prsh9KOKy4kxzexFxfMs5MkteHeWMoswsEB7kiNhyUVkAOFW/qsEzNHtrkgyghrD2ilZJa+5YA==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.3.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-trace-base@2.10.0':
|
||||||
|
resolution: {integrity: sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.3.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-trace-node@2.10.0':
|
||||||
|
resolution: {integrity: sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-trace@2.10.0':
|
||||||
|
resolution: {integrity: sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==}
|
||||||
|
engines: {node: ^18.19.0 || >=20.6.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '>=1.3.0 <1.10.0'
|
||||||
|
|
||||||
|
'@opentelemetry/semantic-conventions@1.43.0':
|
||||||
|
resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
'@pinojs/redact@0.4.0':
|
'@pinojs/redact@0.4.0':
|
||||||
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
||||||
|
|
||||||
|
'@protobufjs/aspromise@1.1.2':
|
||||||
|
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
|
||||||
|
|
||||||
|
'@protobufjs/base64@1.1.2':
|
||||||
|
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
|
||||||
|
|
||||||
|
'@protobufjs/codegen@2.0.5':
|
||||||
|
resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==}
|
||||||
|
|
||||||
|
'@protobufjs/eventemitter@1.1.1':
|
||||||
|
resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==}
|
||||||
|
|
||||||
|
'@protobufjs/fetch@1.1.1':
|
||||||
|
resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==}
|
||||||
|
|
||||||
|
'@protobufjs/float@1.0.2':
|
||||||
|
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
|
||||||
|
|
||||||
|
'@protobufjs/path@1.1.2':
|
||||||
|
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
|
||||||
|
|
||||||
|
'@protobufjs/pool@1.1.0':
|
||||||
|
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
|
||||||
|
|
||||||
|
'@protobufjs/utf8@1.1.2':
|
||||||
|
resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.62.4':
|
'@rollup/rollup-android-arm-eabi@4.62.4':
|
||||||
resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==}
|
resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
@@ -779,6 +1026,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
|
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
|
cjs-module-lexer@2.2.0:
|
||||||
|
resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==}
|
||||||
|
|
||||||
|
cliui@8.0.1:
|
||||||
|
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
@@ -843,6 +1097,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
emoji-regex@8.0.0:
|
||||||
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
end-of-stream@1.4.5:
|
end-of-stream@1.4.5:
|
||||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||||
|
|
||||||
@@ -857,6 +1114,9 @@ packages:
|
|||||||
es-module-lexer@1.7.0:
|
es-module-lexer@1.7.0:
|
||||||
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||||
|
|
||||||
|
es-module-lexer@2.3.1:
|
||||||
|
resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==}
|
||||||
|
|
||||||
es-object-atoms@1.1.2:
|
es-object-atoms@1.1.2:
|
||||||
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
|
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -875,6 +1135,10 @@ packages:
|
|||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
escalade@3.2.0:
|
||||||
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
escape-string-regexp@4.0.0:
|
escape-string-regexp@4.0.0:
|
||||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -1018,6 +1282,10 @@ packages:
|
|||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5:
|
||||||
|
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||||
|
engines: {node: 6.* || 8.* || >= 10.*}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1082,6 +1350,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
import-in-the-middle@3.3.3:
|
||||||
|
resolution: {integrity: sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
imurmurhash@0.1.4:
|
imurmurhash@0.1.4:
|
||||||
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||||
engines: {node: '>=0.8.19'}
|
engines: {node: '>=0.8.19'}
|
||||||
@@ -1101,6 +1373,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0:
|
||||||
|
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
is-glob@4.0.3:
|
is-glob@4.0.3:
|
||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1138,14 +1414,6 @@ packages:
|
|||||||
keyv@4.5.4:
|
keyv@4.5.4:
|
||||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||||
|
|
||||||
langfuse-core@3.38.20:
|
|
||||||
resolution: {integrity: sha512-zBKVmQN/1oT5VWZUBYlWzvokIlkC/6mnpgr/2atMyTeAm+jR3ia7w2iJMjlrF5/oG8ukO1s8+LDRCzJpF1QeEA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
langfuse@3.38.20:
|
|
||||||
resolution: {integrity: sha512-MAmBAASSzJtmK1O9HQegA1mFsQhT8Yf+OJRGvE7FXkyv3g/eiBE0glLD0Ohg3pkxhoPdggM5SejK7ue9ctlaMA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
levn@0.4.1:
|
levn@0.4.1:
|
||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -1157,9 +1425,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
lodash.camelcase@4.3.0:
|
||||||
|
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
|
||||||
|
|
||||||
lodash.merge@4.6.2:
|
lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
|
long@5.3.2:
|
||||||
|
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
|
||||||
|
|
||||||
loupe@3.2.1:
|
loupe@3.2.1:
|
||||||
resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
|
resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
|
||||||
|
|
||||||
@@ -1188,6 +1462,9 @@ packages:
|
|||||||
minimist@1.2.8:
|
minimist@1.2.8:
|
||||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||||
|
|
||||||
|
module-details-from-path@1.0.4:
|
||||||
|
resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
|
||||||
|
|
||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
@@ -1319,6 +1596,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||||
engines: {node: '>= 0.6.0'}
|
engines: {node: '>= 0.6.0'}
|
||||||
|
|
||||||
|
protobufjs@7.6.5:
|
||||||
|
resolution: {integrity: sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
pump@3.0.4:
|
pump@3.0.4:
|
||||||
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
|
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
|
||||||
|
|
||||||
@@ -1340,10 +1621,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
|
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
|
||||||
engines: {node: '>= 12.13.0'}
|
engines: {node: '>= 12.13.0'}
|
||||||
|
|
||||||
|
require-directory@2.1.1:
|
||||||
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
require-from-string@2.0.2:
|
require-from-string@2.0.2:
|
||||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
require-in-the-middle@8.0.1:
|
||||||
|
resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
|
||||||
|
engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
|
||||||
|
|
||||||
resolve-from@4.0.0:
|
resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -1425,6 +1714,10 @@ packages:
|
|||||||
std-env@3.10.0:
|
std-env@3.10.0:
|
||||||
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
string_decoder@1.3.0:
|
string_decoder@1.3.0:
|
||||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||||
|
|
||||||
@@ -1598,9 +1891,30 @@ packages:
|
|||||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
wrap-ansi@7.0.0:
|
||||||
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
|
y18n@5.0.8:
|
||||||
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
yaml@2.9.0:
|
||||||
|
resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
|
||||||
|
engines: {node: '>= 14.6'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
yargs-parser@21.1.1:
|
||||||
|
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
yargs@17.7.3:
|
||||||
|
resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
yocto-queue@0.1.0:
|
yocto-queue@0.1.0:
|
||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -1803,6 +2117,18 @@ snapshots:
|
|||||||
'@fastify/forwarded': 3.0.2
|
'@fastify/forwarded': 3.0.2
|
||||||
ipaddr.js: 2.5.0
|
ipaddr.js: 2.5.0
|
||||||
|
|
||||||
|
'@grpc/grpc-js@1.14.4':
|
||||||
|
dependencies:
|
||||||
|
'@grpc/proto-loader': 0.8.1
|
||||||
|
'@js-sdsl/ordered-map': 4.4.2
|
||||||
|
|
||||||
|
'@grpc/proto-loader@0.8.1':
|
||||||
|
dependencies:
|
||||||
|
lodash.camelcase: 4.3.0
|
||||||
|
long: 5.3.2
|
||||||
|
protobufjs: 7.6.5
|
||||||
|
yargs: 17.7.3
|
||||||
|
|
||||||
'@humanwhocodes/config-array@0.13.0(supports-color@7.2.0)':
|
'@humanwhocodes/config-array@0.13.0(supports-color@7.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@humanwhocodes/object-schema': 2.0.3
|
'@humanwhocodes/object-schema': 2.0.3
|
||||||
@@ -1817,6 +2143,32 @@ snapshots:
|
|||||||
|
|
||||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||||
|
|
||||||
|
'@js-sdsl/ordered-map@4.4.2': {}
|
||||||
|
|
||||||
|
'@langfuse/client@5.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@langfuse/core': 5.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@langfuse/tracing': 5.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
mustache: 4.2.0
|
||||||
|
|
||||||
|
'@langfuse/core@5.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
|
||||||
|
'@langfuse/otel@5.10.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))':
|
||||||
|
dependencies:
|
||||||
|
'@langfuse/core': 5.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-trace-otlp-http': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@langfuse/tracing@5.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@langfuse/core': 5.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
|
||||||
'@napi-rs/lzma-linux-x64-gnu@1.5.1':
|
'@napi-rs/lzma-linux-x64-gnu@1.5.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -1832,8 +2184,252 @@ snapshots:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.20.1
|
fastq: 1.20.1
|
||||||
|
|
||||||
|
'@opentelemetry/api-logs@0.221.0':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
|
||||||
|
'@opentelemetry/api@1.9.1': {}
|
||||||
|
|
||||||
|
'@opentelemetry/configuration@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
yaml: 2.9.0
|
||||||
|
|
||||||
|
'@opentelemetry/context-async-hooks@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
|
||||||
|
'@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-logs-otlp-grpc@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-grpc-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-logs': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-logs-otlp-http@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-logs': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-logs-otlp-proto@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-logs': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-grpc@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-http': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-grpc-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-http@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-proto@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-http': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-prometheus@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-trace-otlp-grpc@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-grpc-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-trace-otlp-proto@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/exporter-zipkin@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
|
||||||
|
'@opentelemetry/instrumentation@0.221.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/api-logs': 0.221.0
|
||||||
|
import-in-the-middle: 3.3.3
|
||||||
|
require-in-the-middle: 8.0.1(supports-color@7.2.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@opentelemetry/otlp-exporter-base@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/otlp-grpc-exporter-base@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@grpc/grpc-js': 1.14.4
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/otlp-transformer@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/api-logs': 0.221.0
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-logs': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/propagator-b3@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/propagator-jaeger@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-logs@0.221.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/api-logs': 0.221.0
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-metrics@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-node@0.221.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/api-logs': 0.221.0
|
||||||
|
'@opentelemetry/configuration': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/context-async-hooks': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-logs-otlp-grpc': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-logs-otlp-http': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-logs-otlp-proto': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-grpc': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-http': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-metrics-otlp-proto': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-prometheus': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-trace-otlp-grpc': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-trace-otlp-http': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-trace-otlp-proto': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/exporter-zipkin': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/instrumentation': 0.221.0(@opentelemetry/api@1.9.1)(supports-color@7.2.0)
|
||||||
|
'@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/otlp-grpc-exporter-base': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/propagator-b3': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/propagator-jaeger': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-logs': 0.221.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace-node': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-trace-node@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/context-async-hooks': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
|
||||||
|
'@opentelemetry/sdk-trace@2.10.0(@opentelemetry/api@1.9.1)':
|
||||||
|
dependencies:
|
||||||
|
'@opentelemetry/api': 1.9.1
|
||||||
|
'@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
|
||||||
|
'@opentelemetry/semantic-conventions': 1.43.0
|
||||||
|
|
||||||
|
'@opentelemetry/semantic-conventions@1.43.0': {}
|
||||||
|
|
||||||
'@pinojs/redact@0.4.0': {}
|
'@pinojs/redact@0.4.0': {}
|
||||||
|
|
||||||
|
'@protobufjs/aspromise@1.1.2': {}
|
||||||
|
|
||||||
|
'@protobufjs/base64@1.1.2': {}
|
||||||
|
|
||||||
|
'@protobufjs/codegen@2.0.5': {}
|
||||||
|
|
||||||
|
'@protobufjs/eventemitter@1.1.1': {}
|
||||||
|
|
||||||
|
'@protobufjs/fetch@1.1.1':
|
||||||
|
dependencies:
|
||||||
|
'@protobufjs/aspromise': 1.1.2
|
||||||
|
|
||||||
|
'@protobufjs/float@1.0.2': {}
|
||||||
|
|
||||||
|
'@protobufjs/path@1.1.2': {}
|
||||||
|
|
||||||
|
'@protobufjs/pool@1.1.0': {}
|
||||||
|
|
||||||
|
'@protobufjs/utf8@1.1.2': {}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.62.4':
|
'@rollup/rollup-android-arm-eabi@4.62.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -2156,6 +2752,14 @@ snapshots:
|
|||||||
|
|
||||||
check-error@2.1.3: {}
|
check-error@2.1.3: {}
|
||||||
|
|
||||||
|
cjs-module-lexer@2.2.0: {}
|
||||||
|
|
||||||
|
cliui@8.0.1:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
wrap-ansi: 7.0.0
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
@@ -2206,6 +2810,8 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
gopd: 1.2.0
|
gopd: 1.2.0
|
||||||
|
|
||||||
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
end-of-stream@1.4.5:
|
end-of-stream@1.4.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
once: 1.4.0
|
once: 1.4.0
|
||||||
@@ -2216,6 +2822,8 @@ snapshots:
|
|||||||
|
|
||||||
es-module-lexer@1.7.0: {}
|
es-module-lexer@1.7.0: {}
|
||||||
|
|
||||||
|
es-module-lexer@2.3.1: {}
|
||||||
|
|
||||||
es-object-atoms@1.1.2:
|
es-object-atoms@1.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -2282,6 +2890,8 @@ snapshots:
|
|||||||
'@esbuild/win32-ia32': 0.28.2
|
'@esbuild/win32-ia32': 0.28.2
|
||||||
'@esbuild/win32-x64': 0.28.2
|
'@esbuild/win32-x64': 0.28.2
|
||||||
|
|
||||||
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
escape-string-regexp@4.0.0: {}
|
escape-string-regexp@4.0.0: {}
|
||||||
|
|
||||||
eslint-scope@7.2.2:
|
eslint-scope@7.2.2:
|
||||||
@@ -2464,6 +3074,8 @@ snapshots:
|
|||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5: {}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
@@ -2532,6 +3144,12 @@ snapshots:
|
|||||||
parent-module: 1.0.1
|
parent-module: 1.0.1
|
||||||
resolve-from: 4.0.0
|
resolve-from: 4.0.0
|
||||||
|
|
||||||
|
import-in-the-middle@3.3.3:
|
||||||
|
dependencies:
|
||||||
|
cjs-module-lexer: 2.2.0
|
||||||
|
es-module-lexer: 2.3.1
|
||||||
|
module-details-from-path: 1.0.4
|
||||||
|
|
||||||
imurmurhash@0.1.4: {}
|
imurmurhash@0.1.4: {}
|
||||||
|
|
||||||
inflight@1.0.6:
|
inflight@1.0.6:
|
||||||
@@ -2545,6 +3163,8 @@ snapshots:
|
|||||||
|
|
||||||
is-extglob@2.1.1: {}
|
is-extglob@2.1.1: {}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0: {}
|
||||||
|
|
||||||
is-glob@4.0.3:
|
is-glob@4.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-extglob: 2.1.1
|
is-extglob: 2.1.1
|
||||||
@@ -2575,14 +3195,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
json-buffer: 3.0.1
|
json-buffer: 3.0.1
|
||||||
|
|
||||||
langfuse-core@3.38.20:
|
|
||||||
dependencies:
|
|
||||||
mustache: 4.2.0
|
|
||||||
|
|
||||||
langfuse@3.38.20:
|
|
||||||
dependencies:
|
|
||||||
langfuse-core: 3.38.20
|
|
||||||
|
|
||||||
levn@0.4.1:
|
levn@0.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
@@ -2598,8 +3210,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
|
|
||||||
|
lodash.camelcase@4.3.0: {}
|
||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
|
long@5.3.2: {}
|
||||||
|
|
||||||
loupe@3.2.1: {}
|
loupe@3.2.1: {}
|
||||||
|
|
||||||
magic-string@0.30.21:
|
magic-string@0.30.21:
|
||||||
@@ -2624,6 +3240,8 @@ snapshots:
|
|||||||
|
|
||||||
minimist@1.2.8: {}
|
minimist@1.2.8: {}
|
||||||
|
|
||||||
|
module-details-from-path@1.0.4: {}
|
||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
mustache@4.2.0: {}
|
mustache@4.2.0: {}
|
||||||
@@ -2750,6 +3368,20 @@ snapshots:
|
|||||||
|
|
||||||
process@0.11.10: {}
|
process@0.11.10: {}
|
||||||
|
|
||||||
|
protobufjs@7.6.5:
|
||||||
|
dependencies:
|
||||||
|
'@protobufjs/aspromise': 1.1.2
|
||||||
|
'@protobufjs/base64': 1.1.2
|
||||||
|
'@protobufjs/codegen': 2.0.5
|
||||||
|
'@protobufjs/eventemitter': 1.1.1
|
||||||
|
'@protobufjs/fetch': 1.1.1
|
||||||
|
'@protobufjs/float': 1.0.2
|
||||||
|
'@protobufjs/path': 1.1.2
|
||||||
|
'@protobufjs/pool': 1.1.0
|
||||||
|
'@protobufjs/utf8': 1.1.2
|
||||||
|
'@types/node': 22.20.1
|
||||||
|
long: 5.3.2
|
||||||
|
|
||||||
pump@3.0.4:
|
pump@3.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
end-of-stream: 1.4.5
|
end-of-stream: 1.4.5
|
||||||
@@ -2771,8 +3403,17 @@ snapshots:
|
|||||||
|
|
||||||
real-require@0.2.0: {}
|
real-require@0.2.0: {}
|
||||||
|
|
||||||
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
require-from-string@2.0.2: {}
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
|
require-in-the-middle@8.0.1(supports-color@7.2.0):
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3(supports-color@7.2.0)
|
||||||
|
module-details-from-path: 1.0.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
ret@0.5.0: {}
|
ret@0.5.0: {}
|
||||||
@@ -2857,6 +3498,12 @@ snapshots:
|
|||||||
|
|
||||||
std-env@3.10.0: {}
|
std-env@3.10.0: {}
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 8.0.0
|
||||||
|
is-fullwidth-code-point: 3.0.0
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
string_decoder@1.3.0:
|
string_decoder@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
@@ -3006,8 +3653,30 @@ snapshots:
|
|||||||
|
|
||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
|
wrap-ansi@7.0.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
|
yaml@2.9.0: {}
|
||||||
|
|
||||||
|
yargs-parser@21.1.1: {}
|
||||||
|
|
||||||
|
yargs@17.7.3:
|
||||||
|
dependencies:
|
||||||
|
cliui: 8.0.1
|
||||||
|
escalade: 3.2.0
|
||||||
|
get-caller-file: 2.0.5
|
||||||
|
require-directory: 2.1.1
|
||||||
|
string-width: 4.2.3
|
||||||
|
y18n: 5.0.8
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
zod@3.25.76: {}
|
zod@3.25.76: {}
|
||||||
|
|||||||
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
allowBuilds:
|
||||||
|
protobufjs: true
|
||||||
@@ -40,7 +40,7 @@ export class RecommendationsController extends Controller {
|
|||||||
: this.config.LLM_ENABLED;
|
: this.config.LLM_ENABLED;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const recommendations = await this.pipeline.runAsync({ count, useLlm });
|
const recommendations = await this.pipeline.runAsync({ count, useLlm }, { trigger: 'api-list' });
|
||||||
reply.send(recommendations);
|
reply.send(recommendations);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reply.code(500).send({ error: String(err) });
|
reply.code(500).send({ error: String(err) });
|
||||||
@@ -49,10 +49,10 @@ export class RecommendationsController extends Controller {
|
|||||||
|
|
||||||
private async handleTrigger(_request: FastifyRequest, reply: FastifyReply) {
|
private async handleTrigger(_request: FastifyRequest, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const recommendations = await this.pipeline.runAsync({
|
const recommendations = await this.pipeline.runAsync(
|
||||||
count: this.config.RECOMMEND_COUNT,
|
{ count: this.config.RECOMMEND_COUNT, useLlm: this.config.LLM_ENABLED },
|
||||||
useLlm: this.config.LLM_ENABLED,
|
{ trigger: 'api-trigger' }
|
||||||
});
|
);
|
||||||
if (this.config.RADARR_ENABLED) {
|
if (this.config.RADARR_ENABLED) {
|
||||||
await this.pipeline.autoRequest(recommendations);
|
await this.pipeline.autoRequest(recommendations);
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ export class RecommendationsController extends Controller {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const recommendations = await this.pipeline.searchAsync(query, this.config.RECOMMEND_COUNT);
|
const recommendations = await this.pipeline.searchAsync(query, this.config.RECOMMEND_COUNT, 'api-search');
|
||||||
reply.send(recommendations);
|
reply.send(recommendations);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reply.code(500).send({ error: String(err) });
|
reply.code(500).send({ error: String(err) });
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ export function scheduleCron(config: AppConfig, pipeline: RecommendationPipeline
|
|||||||
cron.schedule(config.CRON_SCHEDULE, async () => {
|
cron.schedule(config.CRON_SCHEDULE, async () => {
|
||||||
logger.info('Cron: running recommendation pipeline');
|
logger.info('Cron: running recommendation pipeline');
|
||||||
try {
|
try {
|
||||||
const recommendations = await pipeline.runAsync({
|
const recommendations = await pipeline.runAsync(
|
||||||
count: config.RECOMMEND_COUNT,
|
{ count: config.RECOMMEND_COUNT, useLlm: config.LLM_ENABLED },
|
||||||
useLlm: config.LLM_ENABLED,
|
{ trigger: 'cron' }
|
||||||
});
|
);
|
||||||
if (config.RADARR_ENABLED) {
|
if (config.RADARR_ENABLED) {
|
||||||
await pipeline.autoRequest(recommendations);
|
await pipeline.autoRequest(recommendations);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,10 +32,19 @@ async function main(): Promise<void> {
|
|||||||
const promptService = new PromptService(promptGateway, config.LANGFUSE_PROMPT_CACHE_ENABLED);
|
const promptService = new PromptService(promptGateway, config.LANGFUSE_PROMPT_CACHE_ENABLED);
|
||||||
const llmClient = new LlmClient({ baseUrl: config.LLM_BASE_URL, apiKey: config.LLM_API_KEY });
|
const llmClient = new LlmClient({ baseUrl: config.LLM_BASE_URL, apiKey: config.LLM_API_KEY });
|
||||||
|
|
||||||
const jellyfin = new JellyfinClient(config.JELLYFIN_URL, config.JELLYFIN_API_KEY, config.JELLYFIN_USER_ID);
|
const jellyfin = new JellyfinClient(
|
||||||
|
config.JELLYFIN_URL,
|
||||||
|
config.JELLYFIN_API_KEY,
|
||||||
|
config.JELLYFIN_USER_ID
|
||||||
|
);
|
||||||
const tmdb = new TmdbClient(config.TMDB_API_KEY);
|
const tmdb = new TmdbClient(config.TMDB_API_KEY);
|
||||||
const radarr = config.RADARR_ENABLED
|
const radarr = config.RADARR_ENABLED
|
||||||
? new RadarrClient(config.RADARR_URL, config.RADARR_API_KEY, config.RADARR_QUALITY_PROFILE_ID, config.RADARR_ROOT_FOLDER)
|
? new RadarrClient(
|
||||||
|
config.RADARR_URL,
|
||||||
|
config.RADARR_API_KEY,
|
||||||
|
config.RADARR_QUALITY_PROFILE_ID,
|
||||||
|
config.RADARR_ROOT_FOLDER
|
||||||
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const recommenderTool = new MovieRecommenderTool(promptService, llmClient, config.LLM_MODEL);
|
const recommenderTool = new MovieRecommenderTool(promptService, llmClient, config.LLM_MODEL);
|
||||||
@@ -58,12 +67,19 @@ async function main(): Promise<void> {
|
|||||||
radarrEnabled: config.RADARR_ENABLED,
|
radarrEnabled: config.RADARR_ENABLED,
|
||||||
cronEnabled: config.CRON_ENABLED,
|
cronEnabled: config.CRON_ENABLED,
|
||||||
apiEnabled: config.API_ENABLED,
|
apiEnabled: config.API_ENABLED,
|
||||||
|
langfuse: config.LANGFUSE_BASE_URL,
|
||||||
},
|
},
|
||||||
'sugarr starting'
|
'sugarr starting'
|
||||||
);
|
);
|
||||||
|
|
||||||
scheduleCron(config, pipeline, logger);
|
scheduleCron(config, pipeline, logger);
|
||||||
|
|
||||||
|
const shutdown = () => {
|
||||||
|
tracer.shutdown().catch((err) => logger.error({ err }, 'Error shutting down Langfuse tracer'));
|
||||||
|
};
|
||||||
|
process.on('SIGTERM', shutdown);
|
||||||
|
process.on('SIGINT', shutdown);
|
||||||
|
|
||||||
if (config.API_ENABLED) {
|
if (config.API_ENABLED) {
|
||||||
const app = buildServer({
|
const app = buildServer({
|
||||||
secretKey: config.SECRET_KEY,
|
secretKey: config.SECRET_KEY,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
import { LlmPipeline } from '../../core/llm/LlmPipeline';
|
import { LlmPipeline } from '../../core/llm/LlmPipeline';
|
||||||
import type { RunContext } from '../../core/llm/RunContext';
|
import type { RunContext } from '../../core/llm/RunContext';
|
||||||
import type { JellyfinClient } from '../clients/jellyfin/JellyfinClient';
|
import type { JellyfinClient } from '../clients/jellyfin/JellyfinClient';
|
||||||
@@ -114,8 +115,16 @@ export class RecommendationPipeline extends LlmPipeline<RecommendationRequest, R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchAsync(query: string, count: number): Promise<Recommendation[]> {
|
async searchAsync(query: string, count: number, trigger?: string): Promise<Recommendation[]> {
|
||||||
const trace = this.tracer.startTrace(`${this.name}-search`);
|
const sessionId = randomUUID();
|
||||||
|
return this.tracer.runWithSession(
|
||||||
|
{ sessionId, tags: trigger ? [trigger] : undefined },
|
||||||
|
() => this.runSearch(query, count, trigger)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runSearch(query: string, count: number, trigger?: string): Promise<Recommendation[]> {
|
||||||
|
const trace = this.tracer.startTrace(`${this.name}-search`, { trigger });
|
||||||
const ctx: RunContext = { trace, logger: this.logger };
|
const ctx: RunContext = { trace, logger: this.logger };
|
||||||
try {
|
try {
|
||||||
let library: Movie[] = [];
|
let library: Movie[] = [];
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Langfuse } from 'langfuse';
|
import { LangfuseClient } from '@langfuse/client';
|
||||||
import type { PromptFetcher, RawPrompt } from './PromptGateway';
|
import type { PromptFetcher, RawPrompt } from './PromptGateway';
|
||||||
|
|
||||||
export class LangfusePromptGateway implements PromptFetcher {
|
export class LangfusePromptGateway implements PromptFetcher {
|
||||||
private readonly client: Langfuse;
|
private readonly client: LangfuseClient;
|
||||||
|
|
||||||
constructor(opts: { publicKey: string; secretKey: string; baseUrl: string }) {
|
constructor(opts: { publicKey: string; secretKey: string; baseUrl: string }) {
|
||||||
this.client = new Langfuse({
|
this.client = new LangfuseClient({
|
||||||
publicKey: opts.publicKey,
|
publicKey: opts.publicKey,
|
||||||
secretKey: opts.secretKey,
|
secretKey: opts.secretKey,
|
||||||
baseUrl: opts.baseUrl,
|
baseUrl: opts.baseUrl,
|
||||||
@@ -13,7 +13,7 @@ export class LangfusePromptGateway implements PromptFetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetch(promptName: string): Promise<RawPrompt> {
|
async fetch(promptName: string): Promise<RawPrompt> {
|
||||||
const prompt = await this.client.getPrompt(promptName, undefined, { type: 'chat', cacheTtlSeconds: 0 });
|
const prompt = await this.client.prompt.get(promptName, { type: 'chat' });
|
||||||
return {
|
return {
|
||||||
name: prompt.name,
|
name: prompt.name,
|
||||||
version: prompt.version,
|
version: prompt.version,
|
||||||
|
|||||||
@@ -1,33 +1,82 @@
|
|||||||
import { Langfuse } from 'langfuse';
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
||||||
|
import { LangfuseSpanProcessor } from '@langfuse/otel';
|
||||||
|
import { propagateAttributes, startObservation } from '@langfuse/tracing';
|
||||||
|
import type { LangfuseGeneration, LangfuseSpan } from '@langfuse/tracing';
|
||||||
import { Tracer } from '../llm/Tracer';
|
import { Tracer } from '../llm/Tracer';
|
||||||
import type { GenerationHandle, GenerationInput, GenerationOutput, TraceHandle } from '../llm/Tracer';
|
import type {
|
||||||
|
GenerationHandle,
|
||||||
|
GenerationInput,
|
||||||
|
GenerationOutput,
|
||||||
|
SessionInfo,
|
||||||
|
TraceHandle,
|
||||||
|
} from '../llm/Tracer';
|
||||||
|
|
||||||
|
interface OpenAiUsageDetails {
|
||||||
|
prompt_tokens?: number;
|
||||||
|
completion_tokens?: number;
|
||||||
|
total_tokens?: number;
|
||||||
|
prompt_tokens_details?: { cached_tokens: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Langfuse recognizes usage in either a flat generic schema (input/output/
|
||||||
|
* cache_read_input_tokens/...) or OpenAI's native nested schema
|
||||||
|
* (prompt_tokens/completion_tokens/prompt_tokens_details.cached_tokens/...)
|
||||||
|
* - but NOT a mix of the two ("do not mix formats" per Langfuse's docs).
|
||||||
|
* Mixing them previously caused the whole generation.update() payload
|
||||||
|
* (including output) to land wrong, so this stays entirely within the
|
||||||
|
* OpenAI-native schema.
|
||||||
|
*
|
||||||
|
* The installed @langfuse/tracing SDK's usageDetails type is
|
||||||
|
* `Record<string, number> | OpenAiUsage` (a 3-field camelCase shape with
|
||||||
|
* no _details nesting) even though the wire schema (@langfuse/core's
|
||||||
|
* OpenAiCompletionUsageSchema) supports this nested form - a gap in the
|
||||||
|
* SDK's public types, not a real runtime restriction, hence the cast.
|
||||||
|
*/
|
||||||
|
function toUsageDetails(usage: GenerationOutput['usage']): OpenAiUsageDetails | undefined {
|
||||||
|
if (!usage) return undefined;
|
||||||
|
const details: OpenAiUsageDetails = {};
|
||||||
|
if (usage.promptTokens !== undefined) details.prompt_tokens = usage.promptTokens;
|
||||||
|
if (usage.completionTokens !== undefined) details.completion_tokens = usage.completionTokens;
|
||||||
|
if (usage.totalTokens !== undefined) details.total_tokens = usage.totalTokens;
|
||||||
|
if (usage.cachedTokens !== undefined) details.prompt_tokens_details = { cached_tokens: usage.cachedTokens };
|
||||||
|
return Object.keys(details).length > 0 ? details : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export class LangfuseTracer extends Tracer {
|
export class LangfuseTracer extends Tracer {
|
||||||
private readonly client: Langfuse;
|
private readonly processor: LangfuseSpanProcessor;
|
||||||
|
private readonly sdk: NodeSDK;
|
||||||
|
|
||||||
constructor(opts: { publicKey: string; secretKey: string; baseUrl: string }) {
|
constructor(opts: { publicKey: string; secretKey: string; baseUrl: string }) {
|
||||||
super();
|
super();
|
||||||
this.client = new Langfuse({
|
this.processor = new LangfuseSpanProcessor({
|
||||||
publicKey: opts.publicKey,
|
publicKey: opts.publicKey,
|
||||||
secretKey: opts.secretKey,
|
secretKey: opts.secretKey,
|
||||||
baseUrl: opts.baseUrl,
|
baseUrl: opts.baseUrl,
|
||||||
});
|
});
|
||||||
|
this.sdk = new NodeSDK({ spanProcessors: [this.processor] });
|
||||||
|
this.sdk.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
startTrace(name: string, metadata?: Record<string, unknown>): TraceHandle {
|
startTrace(name: string, metadata?: Record<string, unknown>): TraceHandle {
|
||||||
const trace = this.client.trace({ name, metadata });
|
const span: LangfuseSpan = startObservation(name, { metadata });
|
||||||
return {
|
return {
|
||||||
id: trace.id,
|
id: span.traceId,
|
||||||
startGeneration(input: GenerationInput): GenerationHandle {
|
startGeneration(input: GenerationInput): GenerationHandle {
|
||||||
const generation = trace.generation({
|
const generation: LangfuseGeneration = span.startObservation(
|
||||||
name: input.name,
|
input.name,
|
||||||
model: input.model,
|
{ model: input.model, input: input.input, metadata: input.metadata },
|
||||||
input: input.input,
|
{ asType: 'generation' }
|
||||||
metadata: input.metadata,
|
);
|
||||||
});
|
|
||||||
return {
|
return {
|
||||||
end(output: GenerationOutput) {
|
end(output: GenerationOutput) {
|
||||||
generation.end({ output: output.output, usage: output.usage, metadata: output.metadata });
|
generation.update({
|
||||||
|
output: output.output,
|
||||||
|
usageDetails: toUsageDetails(output.usage) as Record<string, number> | undefined,
|
||||||
|
costDetails: output.usage?.cost !== undefined ? { total: output.usage.cost } : undefined,
|
||||||
|
metadata: output.metadata,
|
||||||
|
});
|
||||||
|
generation.end();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -35,6 +84,14 @@ export class LangfuseTracer extends Tracer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async flush(): Promise<void> {
|
async flush(): Promise<void> {
|
||||||
await this.client.flushAsync();
|
await this.processor.forceFlush();
|
||||||
|
}
|
||||||
|
|
||||||
|
runWithSession<T>(session: SessionInfo, fn: () => Promise<T>): Promise<T> {
|
||||||
|
return propagateAttributes({ sessionId: session.sessionId, tags: session.tags }, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
async shutdown(): Promise<void> {
|
||||||
|
await this.sdk.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import OpenAI from 'openai';
|
import OpenAI from 'openai';
|
||||||
|
import type { ChatCompletionCreateParamsNonStreaming } from 'openai/resources/chat/completions';
|
||||||
|
import type { CompletionUsage } from 'openai/resources/completions';
|
||||||
|
|
||||||
export interface ChatMessage {
|
export interface ChatMessage {
|
||||||
role: 'system' | 'user' | 'assistant';
|
role: 'system' | 'user' | 'assistant';
|
||||||
@@ -13,9 +15,28 @@ export interface ChatOptions {
|
|||||||
|
|
||||||
export interface ChatResult {
|
export interface ChatResult {
|
||||||
content: string;
|
content: string;
|
||||||
usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number };
|
usage?: {
|
||||||
|
promptTokens?: number;
|
||||||
|
completionTokens?: number;
|
||||||
|
totalTokens?: number;
|
||||||
|
cachedTokens?: number;
|
||||||
|
cost?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenRouter-specific request/response extensions, not part of OpenAI's
|
||||||
|
* official schema. `usage.include` asks OpenRouter to report its own real
|
||||||
|
* per-request cost (requires Usage Accounting enabled on the OpenRouter
|
||||||
|
* account); other OpenAI-compatible providers generally ignore unknown
|
||||||
|
* top-level request fields. See:
|
||||||
|
* https://langfuse.com/integrations/gateways/openrouter
|
||||||
|
*/
|
||||||
|
type OpenRouterChatCompletionCreateParams = ChatCompletionCreateParamsNonStreaming & {
|
||||||
|
usage?: { include: boolean };
|
||||||
|
};
|
||||||
|
type OpenRouterCompletionUsage = CompletionUsage & { cost?: number };
|
||||||
|
|
||||||
export class LlmClient {
|
export class LlmClient {
|
||||||
private readonly client: OpenAI;
|
private readonly client: OpenAI;
|
||||||
|
|
||||||
@@ -29,20 +50,25 @@ export class LlmClient {
|
|||||||
temperature: options.temperature,
|
temperature: options.temperature,
|
||||||
max_tokens: options.maxTokens,
|
max_tokens: options.maxTokens,
|
||||||
messages,
|
messages,
|
||||||
});
|
usage: { include: true },
|
||||||
|
} as OpenRouterChatCompletionCreateParams);
|
||||||
|
|
||||||
const choice = response.choices[0];
|
const choice = response.choices[0];
|
||||||
if (!choice) {
|
if (!choice) {
|
||||||
throw new Error('LLM returned no choices');
|
throw new Error('LLM returned no choices');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const usage = response.usage as OpenRouterCompletionUsage | undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: choice.message?.content ?? '',
|
content: choice.message?.content ?? '',
|
||||||
usage: response.usage
|
usage: usage
|
||||||
? {
|
? {
|
||||||
promptTokens: response.usage.prompt_tokens,
|
promptTokens: usage.prompt_tokens,
|
||||||
completionTokens: response.usage.completion_tokens,
|
completionTokens: usage.completion_tokens,
|
||||||
totalTokens: response.usage.total_tokens,
|
totalTokens: usage.total_tokens,
|
||||||
|
cachedTokens: usage.prompt_tokens_details?.cached_tokens,
|
||||||
|
cost: usage.cost,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
import type { Logger } from '../logging/logger';
|
import type { Logger } from '../logging/logger';
|
||||||
import type { LlmTool } from './LlmTool';
|
import type { LlmTool } from './LlmTool';
|
||||||
import type { RunContext } from './RunContext';
|
import type { RunContext } from './RunContext';
|
||||||
import type { Tracer } from './Tracer';
|
import type { Tracer } from './Tracer';
|
||||||
|
|
||||||
|
export interface PipelineRunOptions {
|
||||||
|
/** What triggered this run (e.g. "cron", "api-list") — attached as a Langfuse tag. */
|
||||||
|
trigger?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class LlmPipeline<TInput, TOutput> {
|
export abstract class LlmPipeline<TInput, TOutput> {
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly tracer: Tracer,
|
protected readonly tracer: Tracer,
|
||||||
@@ -12,8 +18,12 @@ export abstract class LlmPipeline<TInput, TOutput> {
|
|||||||
protected abstract readonly name: string;
|
protected abstract readonly name: string;
|
||||||
protected abstract runInternal(input: TInput, ctx: RunContext): Promise<TOutput>;
|
protected abstract runInternal(input: TInput, ctx: RunContext): Promise<TOutput>;
|
||||||
|
|
||||||
async runAsync(input: TInput): Promise<TOutput> {
|
async runAsync(input: TInput, options: PipelineRunOptions = {}): Promise<TOutput> {
|
||||||
const trace = this.tracer.startTrace(this.name);
|
const sessionId = randomUUID();
|
||||||
|
return this.tracer.runWithSession(
|
||||||
|
{ sessionId, tags: options.trigger ? [options.trigger] : undefined },
|
||||||
|
async () => {
|
||||||
|
const trace = this.tracer.startTrace(this.name, { trigger: options.trigger });
|
||||||
const ctx: RunContext = { trace, logger: this.logger };
|
const ctx: RunContext = { trace, logger: this.logger };
|
||||||
try {
|
try {
|
||||||
return await this.runInternal(input, ctx);
|
return await this.runInternal(input, ctx);
|
||||||
@@ -21,6 +31,8 @@ export abstract class LlmPipeline<TInput, TOutput> {
|
|||||||
await this.tracer.flush();
|
await this.tracer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected runTool<TToolInput, TToolOutput>(
|
protected runTool<TToolInput, TToolOutput>(
|
||||||
tool: LlmTool<TToolInput, TToolOutput>,
|
tool: LlmTool<TToolInput, TToolOutput>,
|
||||||
|
|||||||
@@ -7,7 +7,13 @@ export interface GenerationInput {
|
|||||||
|
|
||||||
export interface GenerationOutput {
|
export interface GenerationOutput {
|
||||||
output: unknown;
|
output: unknown;
|
||||||
usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number };
|
usage?: {
|
||||||
|
promptTokens?: number;
|
||||||
|
completionTokens?: number;
|
||||||
|
totalTokens?: number;
|
||||||
|
cachedTokens?: number;
|
||||||
|
cost?: number;
|
||||||
|
};
|
||||||
metadata?: Record<string, unknown>;
|
metadata?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +26,18 @@ export interface TraceHandle {
|
|||||||
startGeneration(input: GenerationInput): GenerationHandle;
|
startGeneration(input: GenerationInput): GenerationHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SessionInfo {
|
||||||
|
sessionId: string;
|
||||||
|
tags?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class Tracer {
|
export abstract class Tracer {
|
||||||
abstract startTrace(name: string, metadata?: Record<string, unknown>): TraceHandle;
|
abstract startTrace(name: string, metadata?: Record<string, unknown>): TraceHandle;
|
||||||
abstract flush(): Promise<void>;
|
abstract flush(): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs `fn` with `session` (sessionId/tags) attached to every trace/span
|
||||||
|
* created inside it, so multiple runs can be grouped in the Langfuse UI.
|
||||||
|
*/
|
||||||
|
abstract runWithSession<T>(session: SessionInfo, fn: () => Promise<T>): Promise<T>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ describe('RecommendationsController', () => {
|
|||||||
|
|
||||||
const res = await app.inject({ method: 'GET', url: '/recommendations' });
|
const res = await app.inject({ method: 'GET', url: '/recommendations' });
|
||||||
expect(res.statusCode).toBe(200);
|
expect(res.statusCode).toBe(200);
|
||||||
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true });
|
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true }, { trigger: 'api-list' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('GET /recommendations?count= and force_llm/force_tmdb override config', async () => {
|
it('GET /recommendations?count= and force_llm/force_tmdb override config', async () => {
|
||||||
@@ -25,7 +25,7 @@ describe('RecommendationsController', () => {
|
|||||||
const app = makeApp({ RECOMMEND_COUNT: 10, LLM_ENABLED: false }, { runAsync } as unknown as RecommendationPipeline);
|
const app = makeApp({ RECOMMEND_COUNT: 10, LLM_ENABLED: false }, { runAsync } as unknown as RecommendationPipeline);
|
||||||
|
|
||||||
await app.inject({ method: 'GET', url: '/recommendations?count=3&force_llm=true' });
|
await app.inject({ method: 'GET', url: '/recommendations?count=3&force_llm=true' });
|
||||||
expect(runAsync).toHaveBeenCalledWith({ count: 3, useLlm: true });
|
expect(runAsync).toHaveBeenCalledWith({ count: 3, useLlm: true }, { trigger: 'api-list' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to the configured count for a malformed or non-positive count query param', async () => {
|
it('falls back to the configured count for a malformed or non-positive count query param', async () => {
|
||||||
@@ -33,13 +33,13 @@ describe('RecommendationsController', () => {
|
|||||||
const app = makeApp({ RECOMMEND_COUNT: 10, LLM_ENABLED: true }, { runAsync } as unknown as RecommendationPipeline);
|
const app = makeApp({ RECOMMEND_COUNT: 10, LLM_ENABLED: true }, { runAsync } as unknown as RecommendationPipeline);
|
||||||
|
|
||||||
await app.inject({ method: 'GET', url: '/recommendations?count=abc' });
|
await app.inject({ method: 'GET', url: '/recommendations?count=abc' });
|
||||||
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true });
|
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true }, { trigger: 'api-list' });
|
||||||
|
|
||||||
await app.inject({ method: 'GET', url: '/recommendations?count=-5' });
|
await app.inject({ method: 'GET', url: '/recommendations?count=-5' });
|
||||||
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true });
|
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true }, { trigger: 'api-list' });
|
||||||
|
|
||||||
await app.inject({ method: 'GET', url: '/recommendations?count=0' });
|
await app.inject({ method: 'GET', url: '/recommendations?count=0' });
|
||||||
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true });
|
expect(runAsync).toHaveBeenCalledWith({ count: 10, useLlm: true }, { trigger: 'api-list' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('POST /recommendations/trigger auto-requests via Radarr when enabled', async () => {
|
it('POST /recommendations/trigger auto-requests via Radarr when enabled', async () => {
|
||||||
@@ -75,6 +75,6 @@ describe('RecommendationsController', () => {
|
|||||||
payload: { query: 'sci-fi like Blade Runner' },
|
payload: { query: 'sci-fi like Blade Runner' },
|
||||||
});
|
});
|
||||||
expect(res.statusCode).toBe(200);
|
expect(res.statusCode).toBe(200);
|
||||||
expect(searchAsync).toHaveBeenCalledWith('sci-fi like Blade Runner', 10);
|
expect(searchAsync).toHaveBeenCalledWith('sci-fi like Blade Runner', 10, 'api-search');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ function makeTracer() {
|
|||||||
const tracer = {
|
const tracer = {
|
||||||
startTrace: vi.fn().mockReturnValue(trace),
|
startTrace: vi.fn().mockReturnValue(trace),
|
||||||
flush: vi.fn().mockResolvedValue(undefined),
|
flush: vi.fn().mockResolvedValue(undefined),
|
||||||
|
runWithSession: vi.fn().mockImplementation((_session: unknown, fn: () => unknown) => fn()),
|
||||||
} as unknown as Tracer;
|
} as unknown as Tracer;
|
||||||
return tracer;
|
return tracer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
const compile = vi.fn().mockReturnValue([{ role: 'user', content: 'hi' }]);
|
const compile = vi.fn().mockReturnValue([{ role: 'user', content: 'hi' }]);
|
||||||
const getPrompt = vi.fn().mockResolvedValue({ name: 'greet', version: 2, config: { model: 'llama3' }, compile });
|
const promptGet = vi
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue({ name: 'greet', version: 2, config: { model: 'llama3' }, compile });
|
||||||
|
|
||||||
vi.mock('langfuse', () => ({
|
vi.mock('@langfuse/client', () => ({
|
||||||
Langfuse: vi.fn().mockImplementation(() => ({ getPrompt })),
|
LangfuseClient: vi.fn().mockImplementation(() => ({ prompt: { get: promptGet } })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { LangfusePromptGateway } from '../../../src/core/langfuse/LangfusePromptGateway';
|
import { LangfusePromptGateway } from '../../../src/core/langfuse/LangfusePromptGateway';
|
||||||
|
|
||||||
describe('LangfusePromptGateway', () => {
|
describe('LangfusePromptGateway', () => {
|
||||||
it('fetches a chat prompt from Langfuse bypassing the SDK cache, and adapts it to RawPrompt', async () => {
|
it('fetches a chat prompt from Langfuse and adapts it to RawPrompt', async () => {
|
||||||
const gateway = new LangfusePromptGateway({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
const gateway = new LangfusePromptGateway({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
const raw = await gateway.fetch('greet');
|
const raw = await gateway.fetch('greet');
|
||||||
|
|
||||||
expect(getPrompt).toHaveBeenCalledWith('greet', undefined, { type: 'chat', cacheTtlSeconds: 0 });
|
expect(promptGet).toHaveBeenCalledWith('greet', { type: 'chat' });
|
||||||
expect(raw.name).toBe('greet');
|
expect(raw.name).toBe('greet');
|
||||||
expect(raw.version).toBe(2);
|
expect(raw.version).toBe(2);
|
||||||
expect(raw.config).toEqual({ model: 'llama3' });
|
expect(raw.config).toEqual({ model: 'llama3' });
|
||||||
|
|||||||
@@ -1,42 +1,148 @@
|
|||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
const {
|
||||||
|
forceFlush,
|
||||||
|
shutdown,
|
||||||
|
sdkStart,
|
||||||
|
sdkShutdown,
|
||||||
|
generationUpdate,
|
||||||
|
generationEnd,
|
||||||
|
childStartObservation,
|
||||||
|
startObservation,
|
||||||
|
propagateAttributes,
|
||||||
|
} = vi.hoisted(() => {
|
||||||
|
const generationUpdate = vi.fn();
|
||||||
const generationEnd = vi.fn();
|
const generationEnd = vi.fn();
|
||||||
const generation = vi.fn().mockReturnValue({ end: generationEnd });
|
const generation = { update: generationUpdate, end: generationEnd };
|
||||||
const trace = vi.fn().mockReturnValue({ id: 'lf-trace-1', generation });
|
const childStartObservation = vi.fn().mockReturnValue(generation);
|
||||||
const flushAsync = vi.fn().mockResolvedValue(undefined);
|
const rootSpan = { traceId: 'lf-trace-1', startObservation: childStartObservation };
|
||||||
|
return {
|
||||||
|
forceFlush: vi.fn().mockResolvedValue(undefined),
|
||||||
|
shutdown: vi.fn().mockResolvedValue(undefined),
|
||||||
|
sdkStart: vi.fn(),
|
||||||
|
sdkShutdown: vi.fn().mockResolvedValue(undefined),
|
||||||
|
generationUpdate,
|
||||||
|
generationEnd,
|
||||||
|
childStartObservation,
|
||||||
|
startObservation: vi.fn().mockReturnValue(rootSpan),
|
||||||
|
propagateAttributes: vi.fn().mockImplementation((_attrs: unknown, fn: () => unknown) => fn()),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
vi.mock('langfuse', () => ({
|
vi.mock('@langfuse/otel', () => ({
|
||||||
Langfuse: vi.fn().mockImplementation(() => ({ trace, flushAsync })),
|
LangfuseSpanProcessor: vi.fn().mockImplementation(() => ({ forceFlush, shutdown })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('@opentelemetry/sdk-node', () => ({
|
||||||
|
NodeSDK: vi.fn().mockImplementation(() => ({ start: sdkStart, shutdown: sdkShutdown })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@langfuse/tracing', () => ({ startObservation, propagateAttributes }));
|
||||||
|
|
||||||
import { LangfuseTracer } from '../../../src/core/langfuse/LangfuseTracer';
|
import { LangfuseTracer } from '../../../src/core/langfuse/LangfuseTracer';
|
||||||
|
|
||||||
describe('LangfuseTracer', () => {
|
describe('LangfuseTracer', () => {
|
||||||
it('starts a Langfuse trace and exposes its id', () => {
|
it('starts the OTEL SDK with a LangfuseSpanProcessor on construction', () => {
|
||||||
|
new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
|
expect(sdkStart).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('starts a root observation and exposes its trace id', () => {
|
||||||
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
const handle = tracer.startTrace('my-pipeline', { foo: 'bar' });
|
const handle = tracer.startTrace('my-pipeline', { foo: 'bar' });
|
||||||
|
|
||||||
expect(trace).toHaveBeenCalledWith({ name: 'my-pipeline', metadata: { foo: 'bar' } });
|
expect(startObservation).toHaveBeenCalledWith('my-pipeline', { metadata: { foo: 'bar' } });
|
||||||
expect(handle.id).toBe('lf-trace-1');
|
expect(handle.id).toBe('lf-trace-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('startGeneration creates a Langfuse generation and end() forwards output/usage', () => {
|
it('startGeneration creates a child generation observation and end() forwards output/usage', () => {
|
||||||
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
const handle = tracer.startTrace('my-pipeline');
|
const handle = tracer.startTrace('my-pipeline');
|
||||||
const gen = handle.startGeneration({ name: 'step-1', model: 'llama3', input: ['hi'] });
|
const gen = handle.startGeneration({ name: 'step-1', model: 'llama3', input: ['hi'] });
|
||||||
gen.end({ output: 'done', usage: { totalTokens: 5 } });
|
gen.end({
|
||||||
|
output: 'done',
|
||||||
|
usage: { promptTokens: 10, completionTokens: 5, totalTokens: 15 },
|
||||||
|
});
|
||||||
|
|
||||||
expect(generation).toHaveBeenCalledWith(
|
expect(childStartObservation).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ name: 'step-1', model: 'llama3', input: ['hi'] })
|
'step-1',
|
||||||
|
expect.objectContaining({ model: 'llama3', input: ['hi'] }),
|
||||||
|
{ asType: 'generation' }
|
||||||
);
|
);
|
||||||
expect(generationEnd).toHaveBeenCalledWith(
|
expect(generationUpdate).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ output: 'done', usage: { totalTokens: 5 } })
|
expect.objectContaining({
|
||||||
|
output: 'done',
|
||||||
|
usageDetails: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
|
||||||
|
costDetails: undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(generationEnd).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('forwards an explicit cost (e.g. from OpenRouter) as costDetails.total instead of relying on price-list lookup', () => {
|
||||||
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
|
const handle = tracer.startTrace('my-pipeline');
|
||||||
|
const gen = handle.startGeneration({ name: 'step-1', model: 'openrouter/auto', input: ['hi'] });
|
||||||
|
gen.end({
|
||||||
|
output: 'done',
|
||||||
|
usage: { promptTokens: 10, completionTokens: 5, totalTokens: 15, cost: 0.00042 },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(generationUpdate).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ costDetails: { total: 0.00042 } })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('flush() calls flushAsync on the underlying client', async () => {
|
it('maps cachedTokens to the OpenAI-native nested prompt_tokens_details.cached_tokens, not a mixed flat key', () => {
|
||||||
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
|
const handle = tracer.startTrace('my-pipeline');
|
||||||
|
const gen = handle.startGeneration({ name: 'step-1', model: 'gpt-4o-mini', input: ['hi'] });
|
||||||
|
gen.end({
|
||||||
|
output: 'done',
|
||||||
|
usage: { promptTokens: 100, completionTokens: 20, totalTokens: 120, cachedTokens: 80 },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(generationUpdate).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
usageDetails: {
|
||||||
|
prompt_tokens: 100,
|
||||||
|
completion_tokens: 20,
|
||||||
|
total_tokens: 120,
|
||||||
|
prompt_tokens_details: { cached_tokens: 80 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits usageDetails entirely when no usage is reported', () => {
|
||||||
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
|
const handle = tracer.startTrace('my-pipeline');
|
||||||
|
const gen = handle.startGeneration({ name: 'step-1', model: 'llama3', input: ['hi'] });
|
||||||
|
gen.end({ output: 'done' });
|
||||||
|
|
||||||
|
expect(generationUpdate).toHaveBeenCalledWith(expect.objectContaining({ usageDetails: undefined }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('flush() force-flushes the span processor without shutting down the SDK', async () => {
|
||||||
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
await tracer.flush();
|
await tracer.flush();
|
||||||
expect(flushAsync).toHaveBeenCalledTimes(1);
|
expect(forceFlush).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sdkShutdown).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shutdown() shuts down the underlying OTEL SDK', async () => {
|
||||||
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
|
await tracer.shutdown();
|
||||||
|
expect(sdkShutdown).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runWithSession propagates sessionId/tags to the callback via propagateAttributes and returns its result', async () => {
|
||||||
|
const tracer = new LangfuseTracer({ publicKey: 'pk', secretKey: 'sk', baseUrl: 'http://lf' });
|
||||||
|
const fn = vi.fn().mockResolvedValue('done');
|
||||||
|
|
||||||
|
const result = await tracer.runWithSession({ sessionId: 'session-1', tags: ['cron'] }, fn);
|
||||||
|
|
||||||
|
expect(propagateAttributes).toHaveBeenCalledWith({ sessionId: 'session-1', tags: ['cron'] }, fn);
|
||||||
|
expect(result).toBe('done');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,6 +29,46 @@ describe('LlmClient', () => {
|
|||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
max_tokens: undefined,
|
max_tokens: undefined,
|
||||||
messages: [{ role: 'user', content: 'hi' }],
|
messages: [{ role: 'user', content: 'hi' }],
|
||||||
|
usage: { include: true },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts OpenRouter cost from the response usage when present', async () => {
|
||||||
|
createMock.mockResolvedValue({
|
||||||
|
choices: [{ message: { content: 'hello' } }],
|
||||||
|
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15, cost: 0.00042 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const client = new LlmClient({ baseUrl: 'https://openrouter.ai/api/v1', apiKey: 'key' });
|
||||||
|
const result = await client.chat([{ role: 'user', content: 'hi' }], { model: 'openrouter/auto' });
|
||||||
|
|
||||||
|
expect(result.usage).toEqual({
|
||||||
|
promptTokens: 10,
|
||||||
|
completionTokens: 5,
|
||||||
|
totalTokens: 15,
|
||||||
|
cost: 0.00042,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts cachedTokens from prompt_tokens_details when present', async () => {
|
||||||
|
createMock.mockResolvedValue({
|
||||||
|
choices: [{ message: { content: 'hello' } }],
|
||||||
|
usage: {
|
||||||
|
prompt_tokens: 100,
|
||||||
|
completion_tokens: 20,
|
||||||
|
total_tokens: 120,
|
||||||
|
prompt_tokens_details: { cached_tokens: 80 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const client = new LlmClient({ baseUrl: 'http://localhost:11434/v1' });
|
||||||
|
const result = await client.chat([{ role: 'user', content: 'hi' }], { model: 'gpt-4o-mini' });
|
||||||
|
|
||||||
|
expect(result.usage).toEqual({
|
||||||
|
promptTokens: 100,
|
||||||
|
completionTokens: 20,
|
||||||
|
totalTokens: 120,
|
||||||
|
cachedTokens: 80,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,24 +23,44 @@ class ThrowingPipeline extends LlmPipeline<void, void> {
|
|||||||
function makeTracer(trace: TraceHandle) {
|
function makeTracer(trace: TraceHandle) {
|
||||||
const flush = vi.fn().mockResolvedValue(undefined);
|
const flush = vi.fn().mockResolvedValue(undefined);
|
||||||
const startTrace = vi.fn().mockReturnValue(trace);
|
const startTrace = vi.fn().mockReturnValue(trace);
|
||||||
const tracer = { startTrace, flush } as unknown as Tracer;
|
const runWithSession = vi.fn().mockImplementation((_session: unknown, fn: () => unknown) => fn());
|
||||||
return { tracer, startTrace, flush };
|
const tracer = { startTrace, flush, runWithSession } as unknown as Tracer;
|
||||||
|
return { tracer, startTrace, flush, runWithSession };
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('LlmPipeline', () => {
|
describe('LlmPipeline', () => {
|
||||||
it('opens one trace, runs runInternal with it, and flushes afterward', async () => {
|
it('runs the whole call inside a session scoped to a fresh session id, opens one trace, runs runInternal, and flushes afterward', async () => {
|
||||||
const trace = { id: 'trace-1', startGeneration: vi.fn() } as unknown as TraceHandle;
|
const trace = { id: 'trace-1', startGeneration: vi.fn() } as unknown as TraceHandle;
|
||||||
const { tracer, startTrace, flush } = makeTracer(trace);
|
const { tracer, startTrace, flush, runWithSession } = makeTracer(trace);
|
||||||
const logger = {} as unknown as Logger;
|
const logger = {} as unknown as Logger;
|
||||||
|
|
||||||
const pipeline = new SumPipeline(tracer, logger);
|
const pipeline = new SumPipeline(tracer, logger);
|
||||||
const result = await pipeline.runAsync([1, 2, 3]);
|
const result = await pipeline.runAsync([1, 2, 3]);
|
||||||
|
|
||||||
expect(result).toBe(6);
|
expect(result).toBe(6);
|
||||||
expect(startTrace).toHaveBeenCalledWith('sum-pipeline');
|
expect(runWithSession).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ sessionId: expect.any(String), tags: undefined }),
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
expect(startTrace).toHaveBeenCalledWith('sum-pipeline', { trigger: undefined });
|
||||||
expect(flush).toHaveBeenCalledTimes(1);
|
expect(flush).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tags the session with the given trigger', async () => {
|
||||||
|
const trace = { id: 'trace-1', startGeneration: vi.fn() } as unknown as TraceHandle;
|
||||||
|
const { tracer, startTrace, runWithSession } = makeTracer(trace);
|
||||||
|
const logger = {} as unknown as Logger;
|
||||||
|
|
||||||
|
const pipeline = new SumPipeline(tracer, logger);
|
||||||
|
await pipeline.runAsync([1, 2, 3], { trigger: 'cron' });
|
||||||
|
|
||||||
|
expect(runWithSession).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ tags: ['cron'] }),
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
expect(startTrace).toHaveBeenCalledWith('sum-pipeline', { trigger: 'cron' });
|
||||||
|
});
|
||||||
|
|
||||||
it('still flushes the trace when runInternal throws', async () => {
|
it('still flushes the trace when runInternal throws', async () => {
|
||||||
const trace = { id: 'trace-2', startGeneration: vi.fn() } as unknown as TraceHandle;
|
const trace = { id: 'trace-2', startGeneration: vi.fn() } as unknown as TraceHandle;
|
||||||
const { tracer, flush } = makeTracer(trace);
|
const { tracer, flush } = makeTracer(trace);
|
||||||
|
|||||||
Reference in New Issue
Block a user