From 1f201a093f4ab8dbdc4a569e197e752b2275bb40 Mon Sep 17 00:00:00 2001 From: Stefano Date: Mon, 14 Jul 2025 16:27:29 -0500 Subject: [PATCH] Initial implementation of opentelemetry-llm tool --- .env.example | 59 +- OPENTELEMETRY.md | 301 ++++ README.md | 151 +- examples/opentelemetry-usage.md | 380 ++++ package-lock.json | 1587 ++++++++++++++++- package.json | 13 +- src/config/opentelemetry-llm.config.ts | 130 ++ .../opentelemetry-llm.controller.ts | 74 + src/server/mcpServer.ts | 2 + src/services/opentelemetry-llm.service.ts | 294 +++ src/tools/opentelemetry-llm.tool.ts | 91 + src/tools/posthog-llm.tool.ts | 2 +- src/types/opentelemetry-llm.types.ts | 168 ++ src/{tools => types}/posthog-llm.types.ts | 0 14 files changed, 3191 insertions(+), 61 deletions(-) create mode 100644 OPENTELEMETRY.md create mode 100644 examples/opentelemetry-usage.md create mode 100644 src/config/opentelemetry-llm.config.ts create mode 100644 src/controllers/opentelemetry-llm.controller.ts create mode 100644 src/services/opentelemetry-llm.service.ts create mode 100644 src/tools/opentelemetry-llm.tool.ts create mode 100644 src/types/opentelemetry-llm.types.ts rename src/{tools => types}/posthog-llm.types.ts (100%) diff --git a/.env.example b/.env.example index dac5d07..f30f08c 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,52 @@ -# Enable debug logging +# PostHog Configuration (existing) +POSTHOG_API_KEY=your_posthog_api_key_here +POSTHOG_HOST=https://app.posthog.com + +# OpenTelemetry Configuration +# ========================== + +# Service Configuration +OTEL_SERVICE_NAME=llm-observability-mcp +OTEL_SERVICE_VERSION=1.0.0 +OTEL_ENVIRONMENT=development + +# OTLP Endpoints +# Uncomment and configure based on your backend + +# Jaeger (local development) +# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 + +# New Relic +# OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4318 +# OTEL_EXPORTER_OTLP_HEADERS=api-key=YOUR_NEW_RELIC_LICENSE_KEY + +# Grafana Cloud +# OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp +# OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic YOUR_BASE64_ENCODED_CREDENTIALS + +# Datadog +# OTEL_EXPORTER_OTLP_ENDPOINT=https://api.datadoghq.com/api/v2/series +# OTEL_EXPORTER_OTLP_HEADERS=DD-API-KEY=YOUR_DD_API_KEY + +# Honeycomb +# OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io/v1/traces +# OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=YOUR_API_KEY + +# Lightstep +# OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.lightstep.com:443/api/v2/otel/trace +# OTEL_EXPORTER_OTLP_HEADERS=lightstep-access-token=YOUR_ACCESS_TOKEN + +# Separate endpoints (optional) +# OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318/v1/metrics +# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces +# OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4318/v1/logs + +# Export Configuration +OTEL_METRIC_EXPORT_INTERVAL=10000 +OTEL_METRIC_EXPORT_TIMEOUT=5000 + +# Sampling Configuration +OTEL_TRACES_SAMPLER_ARG=1.0 + +# Debug Mode DEBUG=false - -# Transport mode -TRANSPORT_MODE= - -# PostHog LLM Observability configuration -POSTHOG_API_KEY= -POSTHOG_HOST=https://us.i.posthog.com diff --git a/OPENTELEMETRY.md b/OPENTELEMETRY.md new file mode 100644 index 0000000..ab0f18d --- /dev/null +++ b/OPENTELEMETRY.md @@ -0,0 +1,301 @@ +# OpenTelemetry LLM Observability Integration + +This MCP server now includes comprehensive OpenTelemetry support for LLM observability, compatible with any OpenTelemetry backend including Jaeger, New Relic, Grafana, Datadog, Honeycomb, and more. + +## Features + +- **Universal Compatibility**: Works with any OpenTelemetry-compatible backend +- **Comprehensive Metrics**: Request counts, token usage, latency, error rates +- **Distributed Tracing**: Full request lifecycle tracking with spans +- **Flexible Configuration**: Environment-based configuration for different backends +- **Zero-Code Integration**: Drop-in replacement for existing observability tools + +## Quick Start + +### 1. Install Dependencies + +The OpenTelemetry dependencies are already included in the package.json: + +```bash +npm install +``` + +### 2. Configure Your Backend + +#### Jaeger (Local Development) + +```bash +# Start Jaeger locally +docker run -d --name jaeger \ + -e COLLECTOR_OTLP_ENABLED=true \ + -p 16686:16686 \ + -p 4317:4317 \ + -p 4318:4318 \ + jaegertracing/all-in-one:latest + +# Configure the MCP server +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +export OTEL_SERVICE_NAME=llm-observability-mcp +``` + +#### New Relic + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4318 +export OTEL_EXPORTER_OTLP_HEADERS="api-key=YOUR_NEW_RELIC_LICENSE_KEY" +export OTEL_SERVICE_NAME=llm-observability-mcp +``` + +#### Grafana Cloud + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $(echo -n YOUR_INSTANCE_ID:YOUR_API_KEY | base64)" +export OTEL_SERVICE_NAME=llm-observability-mcp +``` + +#### Datadog + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.datadoghq.com/api/v2/series +export OTEL_EXPORTER_OTLP_HEADERS="DD-API-KEY=YOUR_DD_API_KEY" +export OTEL_SERVICE_NAME=llm-observability-mcp +``` + +### 3. Start the MCP Server + +```bash +# Start with stdio transport +npm run mcp:stdio + +# Start with HTTP transport +npm run mcp:http +``` + +## Usage + +### Using the OpenTelemetry Tool + +The MCP server provides a new tool: `capture_llm_observability_opentelemetry` + +#### Required Parameters + +- `userId`: The distinct ID of the user +- `model`: The model used (e.g., "gpt-4", "claude-3") +- `provider`: The LLM provider (e.g., "openai", "anthropic") + +#### Optional Parameters + +- `traceId`: Trace ID for grouping related events +- `input`: The input to the LLM (messages, prompt, etc.) +- `outputChoices`: The output from the LLM +- `inputTokens`: Number of tokens in the input +- `outputTokens`: Number of tokens in the output +- `latency`: Latency of the LLM call in seconds +- `httpStatus`: HTTP status code of the LLM call +- `baseUrl`: Base URL of the LLM API +- `operationName`: Name of the operation being performed +- `error`: Error message if the request failed +- `errorType`: Type of error (e.g., "rate_limit", "timeout") +- `mcpToolsUsed`: List of MCP tools used during the request + +### Example Usage + +```json +{ + "userId": "user-123", + "model": "gpt-4", + "provider": "openai", + "inputTokens": 150, + "outputTokens": 75, + "latency": 2.5, + "httpStatus": 200, + "operationName": "chat-completion", + "traceId": "trace-abc123" +} +``` + +## Configuration Reference + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `OTEL_SERVICE_NAME` | Service name for OpenTelemetry | `llm-observability-mcp` | +| `OTEL_SERVICE_VERSION` | Service version | `1.0.0` | +| `OTEL_ENVIRONMENT` | Environment name | `development` | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | Default OTLP endpoint | - | +| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | Metrics endpoint | - | +| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Traces endpoint | - | +| `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | Logs endpoint | - | +| `OTEL_EXPORTER_OTLP_HEADERS` | Headers for authentication (format: "key1=value1,key2=value2") | - | +| `OTEL_METRIC_EXPORT_INTERVAL` | Metrics export interval in ms | `10000` | +| `OTEL_METRIC_EXPORT_TIMEOUT` | Metrics export timeout in ms | `5000` | +| `OTEL_TRACES_SAMPLER_ARG` | Sampling ratio (0.0-1.0) | `1.0` | + +### Backend-Specific Configuration + +#### New Relic + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4318 +export OTEL_EXPORTER_OTLP_HEADERS="api-key=YOUR_LICENSE_KEY" +``` + +#### Jaeger + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +``` + +#### Grafana Cloud + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $(echo -n YOUR_INSTANCE_ID:YOUR_API_KEY | base64)" +``` + +#### Honeycomb + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io/v1/traces +export OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=YOUR_API_KEY" +``` + +#### Lightstep + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.lightstep.com:443/api/v2/otel/trace +export OTEL_EXPORTER_OTLP_HEADERS="lightstep-access-token=YOUR_ACCESS_TOKEN" +``` + +## Metrics Collected + +### Counters + +- `llm.requests.total`: Total number of LLM requests +- `llm.tokens.total`: Total tokens used (input + output) + +### Histograms + +- `llm.latency.duration`: Request latency in milliseconds + +### Gauges + +- `llm.requests.active`: Number of active requests + +### Trace Attributes + +- `llm.model`: The model used +- `llm.provider`: The provider name +- `llm.user_id`: The user ID +- `llm.operation`: The operation name +- `llm.input_tokens`: Input token count +- `llm.output_tokens`: Output token count +- `llm.total_tokens`: Total token count +- `llm.latency_ms`: Latency in milliseconds +- `llm.http_status`: HTTP status code +- `llm.base_url`: API base URL +- `llm.error`: Error message (if any) +- `llm.error_type`: Error type classification +- `llm.input`: Input content (optional) +- `llm.output`: Output content (optional) +- `llm.mcp_tools_used`: MCP tools used + +## Testing with Jaeger + +### 1. Start Jaeger + +```bash +docker run -d --name jaeger \ + -e COLLECTOR_OTLP_ENABLED=true \ + -p 16686:16686 \ + -p 4317:4317 \ + -p 4318:4318 \ + jaegertracing/all-in-one:latest +``` + +### 2. Configure MCP Server + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +export OTEL_SERVICE_NAME=llm-observability-mcp +npm run mcp:stdio +``` + +### 3. View Traces + +Open to view traces in Jaeger UI. + +## Migration from PostHog + +The OpenTelemetry tool is designed to be a drop-in replacement for the PostHog tool. Both tools can coexist, allowing for gradual migration: + +1. **PostHog Tool**: `capture_llm_observability` +2. **OpenTelemetry Tool**: `capture_llm_observability_opentelemetry` + +Both tools accept the same parameters, making migration straightforward. + +## Troubleshooting + +### Common Issues + +#### No Data in Backend + +1. Verify endpoint URLs are correct +2. Check authentication headers +3. Ensure network connectivity +4. Check server logs for errors + +#### High Resource Usage + +1. Adjust sampling ratio: `OTEL_TRACES_SAMPLER_ARG=0.1` +2. Increase export intervals: `OTEL_METRIC_EXPORT_INTERVAL=30000` + +#### Missing Traces + +1. Verify OpenTelemetry is enabled (check for endpoint configuration) +2. Check for initialization errors in logs +3. Ensure proper service name configuration + +### Debug Mode + +Enable debug logging: + +```bash +export DEBUG=true +npm run mcp:stdio +``` + +## Advanced Configuration + +### Custom Headers + +```bash +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer token,Custom-Header=value" +``` + +### Multiple Backends + +Configure different endpoints for metrics and traces: + +```bash +export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metrics.example.com +export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://traces.example.com +``` + +### Sampling Configuration + +```bash +# Sample 10% of traces +export OTEL_TRACES_SAMPLER_ARG=0.1 +``` + +## Support + +For issues or questions: + +1. Check the troubleshooting section above +2. Review server logs with `DEBUG=true` +3. Verify OpenTelemetry configuration +4. Test with Jaeger locally first diff --git a/README.md b/README.md index 69133d2..40b1982 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,24 @@ -# LLM Observability MCP for PostHog +# LLM Observability MCP Server [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) -A Model Context Protocol (MCP) server that provides a tool to capture LLM Observability events and send them to PostHog. +A Model Context Protocol (MCP) server that provides comprehensive LLM observability tools supporting both PostHog and OpenTelemetry backends. ## Overview -This project is an MCP server designed to track and observe Large Language Model (LLM) interactions using [PostHog's LLM Observability](https://posthog.com/docs/llm-observability) features. It allows you to capture detailed information about LLM requests, responses, performance, and costs, providing valuable insights into your AI-powered applications. +This project is an MCP server designed to track and observe Large Language Model (LLM) interactions using both [PostHog's LLM Observability](https://posthog.com/docs/llm-observability) and **OpenTelemetry** for universal observability across any backend that supports OpenTelemetry (Jaeger, New Relic, Grafana, Datadog, Honeycomb, etc.). The server can be run as a local process communicating over `stdio` or as a remote `http` server, making it compatible with any MCP client, such as AI-powered IDEs (e.g., VS Code with an MCP extension, Cursor) or custom applications. ## Features -- **Capture LLM Metrics**: Log key details of LLM interactions, including model, provider, latency, token counts, and more. -- **Flexible Transport**: Run as a local `stdio` process for tight IDE integration or as a standalone `http` server for remote access. -- **Dynamic Configuration**: Configure the server easily using environment variables. -- **Easy Integration**: Connect to MCP-compatible IDEs or use the programmatic client for use in any TypeScript/JavaScript application. +- **Dual Backend Support**: Choose between PostHog or OpenTelemetry (or use both) +- **Universal OpenTelemetry**: Works with any OpenTelemetry-compatible backend +- **Comprehensive Metrics**: Request counts, token usage, latency, error rates +- **Distributed Tracing**: Full request lifecycle tracking with spans +- **Flexible Transport**: Run as local `stdio` process or standalone `http` server +- **Dynamic Configuration**: Environment-based configuration for different backends +- **Zero-Code Integration**: Drop-in replacement for existing observability tools ## Installation for Development @@ -46,10 +49,29 @@ Follow these steps to set up the server for local development. The server is configured via environment variables. +### PostHog Configuration + +| Variable | Description | Default | Example | +| ----------------- | --------------------------------------------------------------------------- | --------- | ------------------------------------- | +| `POSTHOG_API_KEY` | Your PostHog Project API Key (required for PostHog tool) | - | `phc_...` | +| `POSTHOG_HOST` | The URL of your PostHog instance | - | `https://us.i.posthog.com` | + +### OpenTelemetry Configuration + +| Variable | Description | Default | Example | +| ------------------------------- | --------------------------------------------------------------------------- | -------------------------- | ------------------------------------- | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry collector endpoint | - | `http://localhost:4318` | +| `OTEL_EXPORTER_OTLP_HEADERS` | Headers for authentication (comma-separated key=value pairs) | - | `api-key=YOUR_KEY` | +| `OTEL_SERVICE_NAME` | Service name for traces and metrics | `llm-observability-mcp` | `my-llm-app` | +| `OTEL_SERVICE_VERSION` | Service version | `1.0.0` | `2.1.0` | +| `OTEL_ENVIRONMENT` | Environment name | `development` | `production` | +| `OTEL_TRACES_SAMPLER_ARG` | Sampling ratio (0.0-1.0) | `1.0` | `0.1` | +| `OTEL_METRIC_EXPORT_INTERVAL` | Metrics export interval in milliseconds | `10000` | `30000` | + +### General Configuration + | Variable | Description | Default | Example | | ----------------- | --------------------------------------------------------------------------- | --------- | ------------------------------------- | -| `POSTHOG_API_KEY` | **Required.** Your PostHog Project API Key. | - | `phc_...` | -| `POSTHOG_HOST` | **Required.** The URL of your PostHog instance. | - | `https://us.i.posthog.com` | | `TRANSPORT_MODE` | The transport protocol to use. Can be `http` or `stdio`. | `http` | `stdio` | | `DEBUG` | Set to `true` to enable detailed debug logging. | `false` | `true` | @@ -177,25 +199,98 @@ async function main() { main().catch(console.error); ``` -## Tool Reference: `capture_llm_observability` +## Available Tools -This is the core tool provided by the server. It captures LLM usage in PostHog for observability, including requests, responses, and performance metrics. +### PostHog Tool: `capture_llm_observability` -### Parameters +Captures LLM usage in PostHog for observability, including requests, responses, and performance metrics. -| Parameter | Type | Required | Description | -| --------------- | ------------------- | -------- | ----------------------------------------------- | -| `userId` | `string` | Yes | The distinct ID of the user. | -| `model` | `string` | Yes | The model used (e.g., `gpt-4`, `claude-3`). | -| `provider` | `string` | Yes | The LLM provider (e.g., `openai`, `anthropic`). | -| `traceId` | `string` | No | The trace ID to group related AI events. | -| `input` | `any` | No | The input to the LLM (e.g., messages, prompt). | -| `outputChoices` | `any` | No | The output choices from the LLM. | -| `inputTokens` | `number` | No | The number of tokens in the input. | -| `outputTokens` | `number` | No | The number of tokens in the output. | -| `latency` | `number` | No | The latency of the LLM call in seconds. | -| `httpStatus` | `number` | No | The HTTP status code of the LLM API call. | -| `baseUrl` | `string` | No | The base URL of the LLM API. | +### OpenTelemetry Tool: `capture_llm_observability_opentelemetry` + +Captures LLM usage using OpenTelemetry for universal observability across any OpenTelemetry-compatible backend. + +### Parameters Comparison + +| Parameter | Type | Required | Description | PostHog | OpenTelemetry | +| --------------- | ------------------- | -------- | ----------------------------------------------- | ------- | ------------- | +| `userId` | `string` | Yes | The distinct ID of the user. | ✅ | ✅ | +| `model` | `string` | Yes | The model used (e.g., `gpt-4`, `claude-3`). | ✅ | ✅ | +| `provider` | `string` | Yes | The LLM provider (e.g., `openai`, `anthropic`). | ✅ | ✅ | +| `traceId` | `string` | No | The trace ID to group related AI events. | ✅ | ✅ | +| `input` | `any` | No | The input to the LLM (e.g., messages, prompt). | ✅ | ✅ | +| `outputChoices` | `any` | No | The output choices from the LLM. | ✅ | ✅ | +| `inputTokens` | `number` | No | The number of tokens in the input. | ✅ | ✅ | +| `outputTokens` | `number` | No | The number of tokens in the output. | ✅ | ✅ | +| `latency` | `number` | No | The latency of the LLM call in seconds. | ✅ | ✅ | +| `httpStatus` | `number` | No | The HTTP status code of the LLM API call. | ✅ | ✅ | +| `baseUrl` | `string` | No | The base URL of the LLM API. | ✅ | ✅ | +| `operationName` | `string` | No | The name of the operation being performed. | ❌ | ✅ | +| `error` | `string` | No | Error message if the request failed. | ❌ | ✅ | +| `errorType` | `string` | No | Type of error (e.g., rate_limit, timeout). | ❌ | ✅ | +| `mcpToolsUsed` | `string[]` | No | List of MCP tools used during the request. | ❌ | ✅ | + +## Quick Start with OpenTelemetry + +### 1. Choose Your Backend + +**For local testing with Jaeger:** + +```bash +# Start Jaeger with OTLP support +docker run -d --name jaeger \ + -e COLLECTOR_OTLP_ENABLED=true \ + -p 16686:16686 \ + -p 4318:4318 \ + jaegertracing/all-in-one:latest +``` + +**For New Relic:** + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4318 +export OTEL_EXPORTER_OTLP_HEADERS="api-key=YOUR_LICENSE_KEY" +``` + +### 2. Configure Environment + +```bash +# Copy example configuration +cp .env.example .env + +# Edit .env with your backend settings +# For Jaeger: +echo "OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318" >> .env +echo "OTEL_SERVICE_NAME=llm-observability-mcp" >> .env +``` + +### 3. Start the Server + +```bash +npm run mcp:http +# or +npm run mcp:stdio +``` + +### 4. Test the Integration + +```bash +# Test with curl +curl -X POST http://localhost:3000/mcp \ + -H "Content-Type: application/json" \ + -d '{ + "tool": "capture_llm_observability_opentelemetry", + "arguments": { + "userId": "test-user", + "model": "gpt-4", + "provider": "openai", + "inputTokens": 100, + "outputTokens": 50, + "latency": 1.5, + "httpStatus": 200, + "operationName": "test-completion" + } + }' +``` ## Development @@ -203,6 +298,12 @@ This is the core tool provided by the server. It captures LLM usage in PostHog f - **Run tests**: `npm test` - **Lint and format**: `npm run lint` and `npm run format` +## Documentation + +- [OpenTelemetry Setup Guide](OPENTELEMETRY.md) - Complete OpenTelemetry configuration +- [Usage Examples](examples/opentelemetry-usage.md) - Practical examples for different backends +- [Environment Configuration](.env.example) - All available configuration options + ## License [MIT License](https://opensource.org/licenses/MIT) diff --git a/examples/opentelemetry-usage.md b/examples/opentelemetry-usage.md new file mode 100644 index 0000000..051145b --- /dev/null +++ b/examples/opentelemetry-usage.md @@ -0,0 +1,380 @@ +# OpenTelemetry LLM Observability Examples + +This document provides practical examples for using the OpenTelemetry LLM observability tool with various backends. + +## Example 1: Basic Jaeger Setup + +### 1. Start Jaeger + +```bash +# Start Jaeger with OTLP support +docker run -d --name jaeger \ + -e COLLECTOR_OTLP_ENABLED=true \ + -p 16686:16686 \ + -p 4317:4317 \ + -p 4318:4318 \ + jaegertracing/all-in-one:latest +``` + +### 2. Configure Environment + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +export OTEL_SERVICE_NAME=llm-observability-mcp +export OTEL_ENVIRONMENT=development +``` + +### 3. Start MCP Server + +```bash +npm run mcp:stdio +``` + +### 4. Test with Claude Desktop + +Add to your Claude Desktop configuration: + +```json +{ + "mcpServers": { + "llm-observability": { + "command": "node", + "args": ["/path/to/llm-observability-mcp/dist/index.js"], + "env": { + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4318", + "OTEL_SERVICE_NAME": "llm-observability-mcp", + "OTEL_ENVIRONMENT": "development" + } + } + } +} +``` + +### 5. View Traces + +Open to see your traces. + +## Example 2: New Relic Integration + +### 1. Get Your License Key + +From New Relic: Account Settings > API Keys > License Key + +### 2. Configure Environment + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4318 +export OTEL_EXPORTER_OTLP_HEADERS="api-key=YOUR_LICENSE_KEY" +export OTEL_SERVICE_NAME=llm-observability-mcp +export OTEL_ENVIRONMENT=production +``` + +### 3. Usage Example + +```json +{ + "tool": "capture_llm_observability_opentelemetry", + "arguments": { + "userId": "user-12345", + "model": "gpt-4", + "provider": "openai", + "inputTokens": 150, + "outputTokens": 75, + "latency": 2.3, + "httpStatus": 200, + "operationName": "chat-completion", + "traceId": "trace-abc123", + "input": "What is the weather like today?", + "outputChoices": ["The weather is sunny and 75°F today."] + } +} +``` + +## Example 3: Grafana Cloud + +### 1. Get Your Credentials + +From Grafana Cloud: Connections > Data Sources > OpenTelemetry + +### 2. Configure Environment + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $(echo -n YOUR_INSTANCE_ID:YOUR_API_KEY | base64)" +export OTEL_SERVICE_NAME=llm-observability-mcp +``` + +### 3. Docker Compose Setup + +```yaml +# docker-compose.yml +version: '3.8' +services: + llm-observability: + build: . + environment: + - OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp + - OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic YOUR_BASE64_ENCODED_CREDENTIALS + - OTEL_SERVICE_NAME=llm-observability-mcp + ports: + - "3000:3000" +``` + +## Example 4: Honeycomb + +### 1. Get Your API Key + +From Honeycomb: Account Settings > API Keys + +### 2. Configure Environment + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io/v1/traces +export OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=YOUR_API_KEY" +export OTEL_SERVICE_NAME=llm-observability-mcp +export OTEL_ENVIRONMENT=production +``` + +## Example 5: Datadog + +### 1. Get Your API Key + +From Datadog: Organization Settings > API Keys + +### 2. Configure Environment + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.datadoghq.com/api/v2/series +export OTEL_EXPORTER_OTLP_HEADERS="DD-API-KEY=YOUR_API_KEY" +export OTEL_SERVICE_NAME=llm-observability-mcp +``` + +## Example 6: Production Configuration + +### Environment Variables + +```bash +# Service Configuration +export OTEL_SERVICE_NAME=llm-observability-mcp +export OTEL_SERVICE_VERSION=1.2.3 +export OTEL_ENVIRONMENT=production + +# Sampling (10% of traces) +export OTEL_TRACES_SAMPLER_ARG=0.1 + +# Export Configuration +export OTEL_METRIC_EXPORT_INTERVAL=30000 +export OTEL_METRIC_EXPORT_TIMEOUT=10000 + +# Backend Configuration +export OTEL_EXPORTER_OTLP_ENDPOINT=https://your-backend.com:4318 +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token,Custom-Header=value" +``` + +### Kubernetes Deployment + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: llm-observability-mcp +spec: + replicas: 3 + selector: + matchLabels: + app: llm-observability-mcp + template: + metadata: + labels: + app: llm-observability-mcp + spec: + containers: + - name: llm-observability-mcp + image: llm-observability-mcp:latest + ports: + - containerPort: 3000 + env: + - name: OTEL_SERVICE_NAME + value: "llm-observability-mcp" + - name: OTEL_SERVICE_VERSION + value: "1.2.3" + - name: OTEL_ENVIRONMENT + value: "production" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: "https://your-backend.com:4318" + - name: OTEL_EXPORTER_OTLP_HEADERS + valueFrom: + secretKeyRef: + name: otel-credentials + key: headers +``` + +## Example 7: Error Handling and Monitoring + +### Error Tracking + +```json +{ + "tool": "capture_llm_observability_opentelemetry", + "arguments": { + "userId": "user-12345", + "model": "gpt-4", + "provider": "openai", + "httpStatus": 429, + "error": "Rate limit exceeded", + "errorType": "rate_limit", + "latency": 0.1, + "operationName": "chat-completion" + } +} +``` + +### Multi-Tool Usage Tracking + +```json +{ + "tool": "capture_llm_observability_opentelemetry", + "arguments": { + "userId": "user-12345", + "model": "gpt-4", + "provider": "openai", + "inputTokens": 500, + "outputTokens": 200, + "latency": 5.2, + "httpStatus": 200, + "operationName": "complex-workflow", + "mcpToolsUsed": ["file_read", "web_search", "code_execution"], + "traceId": "complex-workflow-123" + } +} +``` + +## Example 8: Testing Script + +### Test Script + +```bash +#!/bin/bash +# test-opentelemetry.sh + +# Start Jaeger +echo "Starting Jaeger..." +docker run -d --name jaeger-test \ + -e COLLECTOR_OTLP_ENABLED=true \ + -p 16686:16686 \ + -p 4318:4318 \ + jaegertracing/all-in-one:latest + +# Wait for Jaeger to start +sleep 5 + +# Configure environment +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 +export OTEL_SERVICE_NAME=llm-observability-test +export OTEL_ENVIRONMENT=test + +# Start MCP server in background +echo "Starting MCP server..." +npm run mcp:stdio & + +# Wait for server to start +sleep 3 + +# Test the tool +echo "Testing OpenTelemetry tool..." +curl -X POST http://localhost:3000/mcp \ + -H "Content-Type: application/json" \ + -d '{ + "tool": "capture_llm_observability_opentelemetry", + "arguments": { + "userId": "test-user", + "model": "gpt-4", + "provider": "openai", + "inputTokens": 100, + "outputTokens": 50, + "latency": 1.5, + "httpStatus": 200, + "operationName": "test-completion" + } + }' + +echo "Test complete. View traces at http://localhost:16686" +``` + +## Example 9: Integration with Existing Tools + +### Gradual Migration from PostHog + +You can use both tools simultaneously during migration: + +```json +// PostHog (existing) +{ + "tool": "capture_llm_observability", + "arguments": { + "userId": "user-123", + "model": "gpt-4", + "provider": "openai" + } +} + +// OpenTelemetry (new) +{ + "tool": "capture_llm_observability_opentelemetry", + "arguments": { + "userId": "user-123", + "model": "gpt-4", + "provider": "openai" + } +} +``` + +## Troubleshooting Examples + +### Debug Mode + +```bash +export DEBUG=true +npm run mcp:stdio +``` + +### Check Configuration + +```bash +# Test connectivity +curl -X POST http://localhost:4318/v1/traces \ + -H "Content-Type: application/json" \ + -d '{"resourceSpans":[]}' +``` + +### Verify Environment + +```bash +# Check environment variables +env | grep OTEL +``` + +## Performance Tuning + +### High-Volume Configuration + +```bash +# Reduce sampling for high-volume +export OTEL_TRACES_SAMPLER_ARG=0.01 + +# Increase export intervals +export OTEL_METRIC_EXPORT_INTERVAL=60000 +export OTEL_METRIC_EXPORT_TIMEOUT=30000 +``` + +### Resource Optimization + +```bash +# Disable metrics if only traces needed +unset OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + +# Disable logs if not needed +unset OTEL_EXPORTER_OTLP_LOGS_ENDPOINT +``` + +These examples should help you get started with OpenTelemetry LLM observability across different backends and use cases. diff --git a/package-lock.json b/package-lock.json index e39ce2b..ad1590e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,15 @@ "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-otlp-grpc": "^0.26.0", + "@opentelemetry/exporter-otlp-http": "^0.26.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@opentelemetry/sdk-node": "^0.203.0", + "@opentelemetry/sdk-trace-node": "^2.0.1", + "@opentelemetry/semantic-conventions": "^1.36.0", "commander": "^14.0.0", "cors": "^2.8.5", "dotenv": "^17.2.0", @@ -811,6 +820,182 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@grpc/grpc-js": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", + "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/grpc-js/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/@grpc/grpc-js/node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.13.tgz", + "integrity": "sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==", + "license": "Apache-2.0", + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.11.3", + "yargs": "^16.2.0" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@grpc/proto-loader/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/@grpc/proto-loader/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1443,6 +1628,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.15.1.tgz", @@ -1673,6 +1868,1165 @@ "@octokit/openapi-types": "^25.1.0" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", + "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-metrics": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.26.0.tgz", + "integrity": "sha512-idDSUTx+LRwJiHhVHhdh45SWow5u9lKNDROKu5AMzsIVPI29utH5FfT9vor8qMM6blxWWvlT22HUNdNMWqUQfQ==", + "deprecated": "Please use @opentelemetry/api >= 1.3.0", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.1.tgz", + "integrity": "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.0.0.tgz", + "integrity": "sha512-1+qvKilADnSFW4PiXy+f7D22pvfGVxepZ69GcbF8cTcbQTUt7w63xEBWn5f5j92x9I3c0sqbW1RUx5/a4wgzxA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=8.5.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.0.tgz", + "integrity": "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.203.0.tgz", + "integrity": "sha512-g/2Y2noc/l96zmM+g0LdeuyYKINyBwN6FJySoU15LHPLcMN/1a0wNk2SegwKcxrRdE7Xsm7fkIR5n6XFe3QpPw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/sdk-logs": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.203.0.tgz", + "integrity": "sha512-s0hys1ljqlMTbXx2XiplmMJg9wG570Z5lH7wMvrZX6lcODI56sG4HL03jklF63tBeyNwK2RV1/ntXGo3HgG4Qw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/sdk-logs": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.203.0.tgz", + "integrity": "sha512-nl/7S91MXn5R1aIzoWtMKGvqxgJgepB/sH9qW0rZvZtabnsjbf8OQ1uSx3yogtvLr0GzwD596nQKz2fV7q2RBw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.203.0", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.203.0.tgz", + "integrity": "sha512-FCCj9nVZpumPQSEI57jRAA89hQQgONuoC35Lt+rayWY/mzCAc6BQT7RFyFaZKJ2B7IQ8kYjOCPsF/HGFWjdQkQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.203.0.tgz", + "integrity": "sha512-HFSW10y8lY6BTZecGNpV3GpoSy7eaO0Z6GATwZasnT4bEsILp8UJXNG5OmEsz4SdwCSYvyCbTJdNbZP3/8LGCQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.203.0.tgz", + "integrity": "sha512-OZnhyd9npU7QbyuHXFEPVm3LnjZYifuKpT3kTnF84mXeEQ84pJJZgyLBpU4FSkSwUkt/zbMyNAI7y5+jYTWGIg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-otlp-grpc": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-otlp-grpc/-/exporter-otlp-grpc-0.26.0.tgz", + "integrity": "sha512-64VPck7CbGhI7c2bj54xaGGHCy4mP+rMnrKBAruNBmUUnN9OgihddNcMsIiHyddUcyC1I+hXS3JLW1G6AvlAmg==", + "deprecated": "Please use trace and metric specific exporters @opentelemetry/exporter-trace-otlp-grpc and @opentelemetry/exporter-metrics-otlp-grpc", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.3.7", + "@grpc/proto-loader": "^0.6.4", + "@opentelemetry/core": "1.0.0", + "@opentelemetry/exporter-otlp-http": "0.26.0", + "@opentelemetry/resources": "1.0.0", + "@opentelemetry/sdk-metrics-base": "0.26.0", + "@opentelemetry/sdk-trace-base": "1.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/exporter-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.0.0.tgz", + "integrity": "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.0.0", + "@opentelemetry/semantic-conventions": "1.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/exporter-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.0.tgz", + "integrity": "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/exporter-otlp-http": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-otlp-http/-/exporter-otlp-http-0.26.0.tgz", + "integrity": "sha512-V3FcUEIVDZ66b3/6vjSBjwwozf/XV5eUXuELNzN8PAvGZH4mw36vaWlaxnGEV8HaZb2hbu2KbRpcOzqxx3tFDA==", + "deprecated": "Please use trace and metric specific exporters @opentelemetry/exporter-trace-otlp-http and @opentelemetry/exporter-metrics-otlp-http", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-metrics": "0.26.0", + "@opentelemetry/core": "1.0.0", + "@opentelemetry/resources": "1.0.0", + "@opentelemetry/sdk-metrics-base": "0.26.0", + "@opentelemetry/sdk-trace-base": "1.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/exporter-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.0.0.tgz", + "integrity": "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.0.0", + "@opentelemetry/semantic-conventions": "1.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/exporter-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.0.tgz", + "integrity": "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.203.0.tgz", + "integrity": "sha512-2jLuNuw5m4sUj/SncDf/mFPabUxMZmmYetx5RKIMIQyPnl6G6ooFzfeE8aXNRf8YD1ZXNlCnRPcISxjveGJHNg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.203.0.tgz", + "integrity": "sha512-322coOTf81bm6cAA8+ML6A+m4r2xTCdmAZzGNTboPXRzhwPt4JEmovsFAs+grpdarObd68msOJ9FfH3jxM6wqA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.203.0.tgz", + "integrity": "sha512-ZDiaswNYo0yq/cy1bBLJFe691izEJ6IgNmkjm4C6kE9ub/OMQqDXORx2D2j8fzTBTxONyzusbaZlqtfmyqURPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.203.0.tgz", + "integrity": "sha512-1xwNTJ86L0aJmWRwENCJlH4LULMG2sOXWIVw+Szta4fkqKVY50Eo4HoVKKq6U9QEytrWCr8+zjw0q/ZOeXpcAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.0.1.tgz", + "integrity": "sha512-a9eeyHIipfdxzCfc2XPrE+/TI3wmrZUDFtG2RRXHSbZZULAny7SyybSvaDvS77a7iib5MPiAvluwVvbGTsHxsw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", + "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.203.0.tgz", + "integrity": "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-transformer": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.203.0.tgz", + "integrity": "sha512-te0Ze1ueJF+N/UOFl5jElJW4U0pZXQ8QklgSfJ2linHN0JJsuaHG8IabEUi2iqxY8ZBDlSiz1Trfv5JcjWWWwQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.203.0", + "@opentelemetry/otlp-transformer": "0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.203.0.tgz", + "integrity": "sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.203.0", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.0.1.tgz", + "integrity": "sha512-Hc09CaQ8Tf5AGLmf449H726uRoBNGPBL4bjr7AnnUpzWMvhdn61F78z9qb6IqB737TffBsokGAK1XykFEZ1igw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.0.1.tgz", + "integrity": "sha512-7PMdPBmGVH2eQNb/AtSJizQNgeNTfh6jQFqys6lfhd6P4r+m/nTh3gKPPpaCXVdRQ+z93vfKk+4UGty390283w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.203.0.tgz", + "integrity": "sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics-base": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.26.0.tgz", + "integrity": "sha512-PbJsso7Vy/CLATAOyXbt/VP7ZQ2QYnvlq28lhOWaLPw8aqLogMBvidNGRrt7rF4/hfzLT6pMgpAAcit2C/nUMA==", + "deprecated": "Please use @opentelemetry/sdk-metrics", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-metrics": "0.26.0", + "@opentelemetry/core": "1.0.0", + "@opentelemetry/resources": "1.0.0", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/sdk-metrics-base/node_modules/@opentelemetry/resources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.0.0.tgz", + "integrity": "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.0.0", + "@opentelemetry/semantic-conventions": "1.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/sdk-metrics-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.0.tgz", + "integrity": "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.203.0.tgz", + "integrity": "sha512-zRMvrZGhGVMvAbbjiNQW3eKzW/073dlrSiAKPVWmkoQzah9wfynpVPeL55f9fVIm0GaBxTLcPeukWGy0/Wj7KQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-logs-otlp-grpc": "0.203.0", + "@opentelemetry/exporter-logs-otlp-http": "0.203.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.203.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.203.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.203.0", + "@opentelemetry/exporter-prometheus": "0.203.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.203.0", + "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.203.0", + "@opentelemetry/exporter-zipkin": "2.0.1", + "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/propagator-b3": "2.0.1", + "@opentelemetry/propagator-jaeger": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.203.0", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "@opentelemetry/sdk-trace-node": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.0.0.tgz", + "integrity": "sha512-/rXoyQlDlJTJ4SOVAbP0Gpj89B8oZ2hJApYG2Dq5klkgFAtDifN8271TIzwtM8/ET8HUhgx9eyoUJi42LhIesg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.0.0", + "@opentelemetry/resources": "1.0.0", + "@opentelemetry/semantic-conventions": "1.0.0", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.0.0.tgz", + "integrity": "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.0.0", + "@opentelemetry/semantic-conventions": "1.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.2" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.0.tgz", + "integrity": "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.1.tgz", + "integrity": "sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.0.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz", + "integrity": "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1742,6 +3096,70 @@ "node": ">=12" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", @@ -2404,6 +3822,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -2415,7 +3839,6 @@ "version": "24.0.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.8.0" @@ -3023,7 +4446,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3032,6 +4454,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -3115,7 +4546,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3741,7 +5171,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -3756,7 +5185,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3766,14 +5194,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -3788,7 +5214,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -3801,7 +5226,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -3837,7 +5261,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3850,7 +5273,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/commander": { @@ -4541,7 +5963,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5346,7 +6767,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -5773,6 +7193,24 @@ "node": ">=18.20" } }, + "node_modules/import-in-the-middle": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.2.tgz", + "integrity": "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/import-in-the-middle/node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "license": "MIT" + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -5895,6 +7333,21 @@ "dev": true, "license": "MIT" }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -5909,7 +7362,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7129,6 +8581,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, "node_modules/lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", @@ -7168,7 +8626,6 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, "license": "MIT" }, "node_modules/lodash.uniqby": { @@ -7178,6 +8635,12 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7442,6 +8905,12 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -10593,6 +12062,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -10947,6 +12422,32 @@ "dev": true, "license": "ISC" }, + "node_modules/protobufjs": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -11196,12 +12697,45 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -11485,7 +13019,6 @@ "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -12169,6 +13702,18 @@ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/synckit": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", @@ -12585,7 +14130,6 @@ "version": "7.8.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true, "license": "MIT" }, "node_modules/unicode-emoji-modifier-base": { @@ -12964,7 +14508,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -12981,7 +14524,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -13000,7 +14542,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -13010,7 +14551,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13020,14 +14560,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -13042,7 +14580,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" diff --git a/package.json b/package.json index 4d147f7..4da9b46 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@sfiorini/llm-observability-mcp", "version": "0.1.0", - "description": "A Model Context Protocol (MCP) server that provides a tool to capture LLM Observability events and send them to PostHog.", + "description": "A Model Context Protocol (MCP) server that provides comprehensive LLM observability tools supporting both PostHog and OpenTelemetry backends.", "main": "dist/index.js", "types": "dist/index.d.ts", "type": "commonjs", @@ -76,6 +76,15 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-otlp-grpc": "^0.26.0", + "@opentelemetry/exporter-otlp-http": "^0.26.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@opentelemetry/sdk-node": "^0.203.0", + "@opentelemetry/sdk-trace-node": "^2.0.1", + "@opentelemetry/semantic-conventions": "^1.36.0", "commander": "^14.0.0", "cors": "^2.8.5", "dotenv": "^17.2.0", @@ -113,4 +122,4 @@ ".ts" ] } -} \ No newline at end of file +} diff --git a/src/config/opentelemetry-llm.config.ts b/src/config/opentelemetry-llm.config.ts new file mode 100644 index 0000000..81543f9 --- /dev/null +++ b/src/config/opentelemetry-llm.config.ts @@ -0,0 +1,130 @@ +import { z } from 'zod'; + +/** + * Configuration schema for OpenTelemetry + */ +export const OpenTelemetryConfigSchema = z.object({ + // Service configuration + serviceName: z.string().default('llm-observability-mcp'), + serviceVersion: z.string().default('1.0.0'), + environment: z.string().default('development'), + + // OTLP endpoints + metricsEndpoint: z.string().optional(), + tracesEndpoint: z.string().optional(), + logsEndpoint: z.string().optional(), + + // Authentication headers + headers: z.record(z.string()).optional(), + + // Export configuration + exportIntervalMillis: z.number().default(10000), + exportTimeoutMillis: z.number().default(5000), + + // Sampling configuration + samplingRatio: z.number().min(0).max(1).default(1.0), +}); + +export type OpenTelemetryConfigType = z.infer; + +/** + * OpenTelemetry configuration class + */ +export class OpenTelemetryConfig { + public readonly serviceName: string; + public readonly serviceVersion: string; + public readonly environment: string; + public readonly metricsEndpoint?: string; + public readonly tracesEndpoint?: string; + public readonly logsEndpoint?: string; + public readonly headers?: Record; + public readonly exportIntervalMillis: number; + public readonly exportTimeoutMillis: number; + public readonly samplingRatio: number; + + constructor(config: OpenTelemetryConfigType) { + this.serviceName = config.serviceName; + this.serviceVersion = config.serviceVersion; + this.environment = config.environment; + this.metricsEndpoint = config.metricsEndpoint; + this.tracesEndpoint = config.tracesEndpoint; + this.logsEndpoint = config.logsEndpoint; + this.headers = config.headers; + this.exportIntervalMillis = config.exportIntervalMillis; + this.exportTimeoutMillis = config.exportTimeoutMillis; + this.samplingRatio = config.samplingRatio; + } + + /** + * Create configuration from environment variables + */ + public static fromEnv(): OpenTelemetryConfig { + const config = OpenTelemetryConfigSchema.parse({ + serviceName: + process.env.OTEL_SERVICE_NAME || 'llm-observability-mcp', + serviceVersion: process.env.OTEL_SERVICE_VERSION || '1.0.0', + environment: process.env.OTEL_ENVIRONMENT || 'development', + metricsEndpoint: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, + tracesEndpoint: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, + logsEndpoint: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, + headers: process.env.OTEL_EXPORTER_OTLP_HEADERS + ? parseHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS) + : undefined, + exportIntervalMillis: parseInt( + process.env.OTEL_METRIC_EXPORT_INTERVAL || '10000', + 10, + ), + exportTimeoutMillis: parseInt( + process.env.OTEL_METRIC_EXPORT_TIMEOUT || '5000', + 10, + ), + samplingRatio: parseFloat( + process.env.OTEL_TRACES_SAMPLER_ARG || '1.0', + ), + }); + + return new OpenTelemetryConfig(config); + } + + /** + * Check if OpenTelemetry is enabled + */ + public isEnabled(): boolean { + return !!( + this.metricsEndpoint || + this.tracesEndpoint || + this.logsEndpoint + ); + } + + /** + * Get combined OTLP endpoint if only one is provided + */ + public getDefaultEndpoint(): string | undefined { + if ( + this.metricsEndpoint && + this.tracesEndpoint === this.metricsEndpoint + ) { + return this.metricsEndpoint; + } + return this.metricsEndpoint || this.tracesEndpoint; + } +} + +/** + * Parse headers from environment variable string + * Format: "key1=value1,key2=value2" + */ +function parseHeaders(headersString: string): Record { + const headers: Record = {}; + + const pairs = headersString.split(','); + for (const pair of pairs) { + const [key, value] = pair.split('='); + if (key && value) { + headers[key.trim()] = value.trim(); + } + } + + return headers; +} diff --git a/src/controllers/opentelemetry-llm.controller.ts b/src/controllers/opentelemetry-llm.controller.ts new file mode 100644 index 0000000..34daa87 --- /dev/null +++ b/src/controllers/opentelemetry-llm.controller.ts @@ -0,0 +1,74 @@ +import { Logger } from '../utils/logger.util.js'; +import { OpenTelemetryService } from '../services/opentelemetry-llm.service.js'; +import { OpenTelemetryLlmInputSchemaType } from '../types/opentelemetry-llm.types.js'; +import { ControllerResponse } from '../types/common.types.js'; + +/** + * Controller for OpenTelemetry LLM observability + */ +export class OpenTelemetryController { + private service: OpenTelemetryService; + + constructor(service: OpenTelemetryService) { + this.service = service; + } + + /** + * Capture LLM observability data + */ + public async captureLlmObservability( + data: OpenTelemetryLlmInputSchemaType, + ): Promise { + const logger = Logger.forContext( + 'opentelemetry.controller', + 'captureLlmObservability', + ); + logger.debug('Capturing LLM observability data', data); + + try { + // Record the LLM request + this.service.recordLlmRequest(data); + + const content = `## OpenTelemetry LLM Observability Captured + +Successfully recorded LLM observability data: + +- **Model**: ${data.model} +- **Provider**: ${data.provider} +- **User ID**: ${data.userId} +- **Operation**: ${data.operationName || 'generate'} +- **Input Tokens**: ${data.inputTokens || 'N/A'} +- **Output Tokens**: ${data.outputTokens || 'N/A'} +- **Total Tokens**: ${(data.inputTokens || 0) + (data.outputTokens || 0)} +- **Latency**: ${data.latency ? `${data.latency}s` : 'N/A'} +- **Status**: ${data.error ? 'Error' : 'Success'} +${data.error ? `- **Error**: ${data.error}` : ''} + +### Metrics Recorded +- ✅ Request counter incremented +- ✅ Token usage recorded +- ✅ Latency histogram updated +- ✅ Distributed trace created + +### Trace Details +- **Trace ID**: ${data.traceId || 'auto-generated'} +- **Span Name**: ${data.operationName || 'llm.generate'} +- **Attributes**: model, provider, user_id, tokens, latency, error details + +The data has been sent to your configured OpenTelemetry collector and is now available in your observability platform (Jaeger, New Relic, Grafana, etc.).`; + + return { content }; + } catch (error) { + logger.error('Error capturing LLM observability', error); + throw error; + } + } +} + +// Export singleton instance +const openTelemetryService = OpenTelemetryService.getInstance(); +const openTelemetryController = new OpenTelemetryController( + openTelemetryService, +); + +export default openTelemetryController; diff --git a/src/server/mcpServer.ts b/src/server/mcpServer.ts index feb7b41..7cf3fb3 100644 --- a/src/server/mcpServer.ts +++ b/src/server/mcpServer.ts @@ -7,6 +7,7 @@ import { config } from '../utils/config.util'; import { PACKAGE_NAME, VERSION } from '../utils/constants.util'; import posthogLlmResources from '../resources/posthog-llm.resource.js'; import posthogLlmTools from '../tools/posthog-llm.tool.js'; +import openTelemetryTools from '../tools/opentelemetry-llm.tool.js'; export function createServer() { const serverLogger = Logger.forContext('utils/server.util.ts', 'getServer'); @@ -29,6 +30,7 @@ export function createServer() { serverLogger.info('Registering MCP tools and resources...'); posthogLlmTools.registerTools(server); posthogLlmResources.registerResources(server); + openTelemetryTools.registerTools(server); serverLogger.debug('All tools and resources registered'); return server; diff --git a/src/services/opentelemetry-llm.service.ts b/src/services/opentelemetry-llm.service.ts new file mode 100644 index 0000000..11f1809 --- /dev/null +++ b/src/services/opentelemetry-llm.service.ts @@ -0,0 +1,294 @@ +import { + MeterProvider, + PeriodicExportingMetricReader, +} from '@opentelemetry/sdk-metrics'; +import { NodeSDK } from '@opentelemetry/sdk-node'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { resourceFromAttributes } from '@opentelemetry/resources'; +import { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, + SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, +} from '@opentelemetry/semantic-conventions'; +import { Logger } from '../utils/logger.util.js'; +import { OpenTelemetryConfig } from '../config/opentelemetry-llm.config.js'; +import { OpenTelemetryLlmInputSchemaType } from '../types/opentelemetry-llm.types.js'; +import { + Meter, + Counter, + Histogram, + UpDownCounter, + metrics, +} from '@opentelemetry/api'; +import { trace, Tracer, SpanStatusCode } from '@opentelemetry/api'; + +/** + * OpenTelemetry service for LLM observability + */ +export class OpenTelemetryService { + private static instance: OpenTelemetryService; + private sdk: NodeSDK | null = null; + private meterProvider: MeterProvider | null = null; + private tracer: Tracer; + private meter: Meter; + private config: OpenTelemetryConfig; + + // Metrics + private requestCounter: Counter; + private tokenCounter: Counter; + private latencyHistogram: Histogram; + private activeRequests: UpDownCounter; + + private constructor(config: OpenTelemetryConfig) { + this.config = config; + this.tracer = trace.getTracer('llm-observability-mcp'); + this.meter = this.initializeMetrics(); + + // Initialize metrics + this.requestCounter = this.meter.createCounter('llm.requests.total', { + description: 'Total number of LLM requests', + }); + + this.tokenCounter = this.meter.createCounter('llm.tokens.total', { + description: 'Total number of tokens processed', + }); + + this.latencyHistogram = this.meter.createHistogram( + 'llm.latency.duration', + { + description: 'Duration of LLM requests in milliseconds', + unit: 'ms', + }, + ); + + this.activeRequests = this.meter.createUpDownCounter( + 'llm.requests.active', + { + description: 'Number of active LLM requests', + }, + ); + } + + /** + * Get singleton instance + */ + public static getInstance( + config?: OpenTelemetryConfig, + ): OpenTelemetryService { + if (!OpenTelemetryService.instance) { + if (!config) { + throw new Error( + 'OpenTelemetryService requires configuration on first initialization', + ); + } + OpenTelemetryService.instance = new OpenTelemetryService(config); + } + return OpenTelemetryService.instance; + } + + /** + * Initialize OpenTelemetry SDK + */ + public initialize(): void { + const logger = Logger.forContext('opentelemetry.service', 'initialize'); + + if (this.sdk) { + logger.warn('OpenTelemetry SDK already initialized'); + return; + } + + try { + const resource = resourceFromAttributes({ + [ATTR_SERVICE_NAME]: this.config.serviceName, + [ATTR_SERVICE_VERSION]: this.config.serviceVersion, + [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: this.config.environment, + }); + + // Configure metric exporter + const metricExporter = this.config.metricsEndpoint + ? new OTLPMetricExporter({ + url: this.config.metricsEndpoint, + headers: this.config.headers, + }) + : undefined; + + // Configure trace exporter + const traceExporter = this.config.tracesEndpoint + ? new OTLPTraceExporter({ + url: this.config.tracesEndpoint, + headers: this.config.headers, + }) + : undefined; + + // Initialize meter provider + if (metricExporter) { + this.meterProvider = new MeterProvider({ + resource, + readers: [ + new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: + this.config.exportIntervalMillis, + exportTimeoutMillis: + this.config.exportTimeoutMillis, + }), + ], + }); + } + + // Initialize SDK + this.sdk = new NodeSDK({ + resource, + spanProcessor: traceExporter + ? new BatchSpanProcessor(traceExporter) + : undefined, + }); + + this.sdk.start(); + logger.info('OpenTelemetry SDK initialized successfully'); + } catch (error) { + logger.error('Failed to initialize OpenTelemetry SDK', error); + throw error; + } + } + + /** + * Initialize metrics + */ + private initializeMetrics(): Meter { + if (this.meterProvider) { + return this.meterProvider.getMeter('llm-observability-mcp'); + } + return metrics.getMeter('llm-observability-mcp'); + } + + /** + * Record LLM request metrics and traces + */ + public recordLlmRequest(data: OpenTelemetryLlmInputSchemaType): void { + const labels = { + model: data.model, + provider: data.provider, + userId: data.userId, + operationName: data.operationName || 'generate', + status: data.error ? 'error' : 'success', + errorType: data.errorType, + mcpToolsUsed: data.mcpToolsUsed?.join(',') || '', + }; + + // Record metrics + this.requestCounter.add(1, labels); + + if (data.inputTokens || data.outputTokens) { + const totalTokens = + (data.inputTokens || 0) + (data.outputTokens || 0); + this.tokenCounter.add(totalTokens, { + ...labels, + type: 'total', + }); + + if (data.inputTokens) { + this.tokenCounter.add(data.inputTokens, { + ...labels, + type: 'input', + }); + } + + if (data.outputTokens) { + this.tokenCounter.add(data.outputTokens, { + ...labels, + type: 'output', + }); + } + } + + if (data.latency) { + this.latencyHistogram.record(data.latency * 1000, labels); + } + + // Create trace + this.createLlmTrace(data); + } + + /** + * Create distributed trace for LLM request + */ + private createLlmTrace(data: OpenTelemetryLlmInputSchemaType): void { + const spanName = data.operationName || 'llm.generate'; + const span = this.tracer.startSpan(spanName, { + attributes: { + 'llm.model': data.model, + 'llm.provider': data.provider, + 'llm.user_id': data.userId, + 'llm.operation': data.operationName || 'generate', + 'llm.input_tokens': data.inputTokens, + 'llm.output_tokens': data.outputTokens, + 'llm.total_tokens': + (data.inputTokens || 0) + (data.outputTokens || 0), + 'llm.latency_ms': data.latency + ? data.latency * 1000 + : undefined, + 'llm.http_status': data.httpStatus, + 'llm.base_url': data.baseUrl, + 'llm.error': data.error, + 'llm.error_type': data.errorType, + 'llm.input': + typeof data.input === 'string' + ? data.input + : JSON.stringify(data.input), + 'llm.output': + typeof data.outputChoices === 'string' + ? data.outputChoices + : JSON.stringify(data.outputChoices), + 'llm.mcp_tools_used': data.mcpToolsUsed?.join(','), + }, + }); + + if (data.traceId) { + span.setAttribute('trace.id', data.traceId); + } + + if (data.error) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: data.error, + }); + } else { + span.setStatus({ code: SpanStatusCode.OK }); + } + + span.end(); + } + + /** + * Record active request count + */ + public incrementActiveRequests(): void { + this.activeRequests.add(1); + } + + /** + * Decrement active request count + */ + public decrementActiveRequests(): void { + this.activeRequests.add(-1); + } + + /** + * Shutdown OpenTelemetry SDK + */ + public async shutdown(): Promise { + const logger = Logger.forContext('opentelemetry.service', 'shutdown'); + + if (this.sdk) { + try { + await this.sdk.shutdown(); + logger.info('OpenTelemetry SDK shutdown successfully'); + } catch (error) { + logger.error('Error shutting down OpenTelemetry SDK', error); + } + } + } +} diff --git a/src/tools/opentelemetry-llm.tool.ts b/src/tools/opentelemetry-llm.tool.ts new file mode 100644 index 0000000..d7d5f96 --- /dev/null +++ b/src/tools/opentelemetry-llm.tool.ts @@ -0,0 +1,91 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { Logger } from '../utils/logger.util.js'; +import { formatErrorForMcpTool } from '../utils/error.util.js'; +import { OpenTelemetryService } from '../services/opentelemetry-llm.service.js'; +import { OpenTelemetryController } from '../controllers/opentelemetry-llm.controller.js'; +import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import { + OpenTelemetryLlmInputSchema, + OpenTelemetryLlmInputSchemaType, +} from '../types/opentelemetry-llm.types.js'; +import { OpenTelemetryConfig } from '../config/opentelemetry-llm.config.js'; + +/** + * @function captureOpenTelemetryLlmObservability + * @description MCP Tool handler to capture LLM observability events using OpenTelemetry. + * It records metrics, traces, and spans for LLM requests that can be sent to any OpenTelemetry-compatible backend. + * @param {OpenTelemetryLlmInputSchemaType} args - Arguments provided to the tool. + * @returns {Promise} Formatted response for the MCP. + * @throws {McpError} Formatted error if the controller or service layer encounters an issue. + */ +async function captureOpenTelemetryLlmObservability( + args: OpenTelemetryLlmInputSchemaType, +): Promise { + const methodLogger = Logger.forContext( + 'opentelemetry.tool', + 'captureOpenTelemetryLlmObservability', + ); + methodLogger.debug('Capture LLM Observability with OpenTelemetry...', args); + + try { + // Parse and validate arguments + const validatedArgs = OpenTelemetryLlmInputSchema.parse(args); + + // Ensure OpenTelemetry is configured + const config = OpenTelemetryConfig.fromEnv(); + const service = OpenTelemetryService.getInstance(config); + + // Initialize if not already done + service.initialize(); + + // Create controller with the service + const controller = new OpenTelemetryController(service); + + // Pass validated args to the controller + const result = await controller.captureLlmObservability(validatedArgs); + methodLogger.debug('Got response from controller', result); + + // Format the response for the MCP tool + return { + content: [ + { + type: 'text' as const, + text: result.content, + }, + ], + }; + } catch (error) { + methodLogger.error( + 'Error tracking LLM generation with OpenTelemetry', + error, + ); + return formatErrorForMcpTool(error); + } +} + +/** + * @function registerTools + * @description Registers the OpenTelemetry LLM observability tool with the MCP server. + * + * @param {McpServer} server - The MCP server instance. + */ +function registerTools(server: McpServer) { + const methodLogger = Logger.forContext( + 'opentelemetry.tool', + 'registerTools', + ); + methodLogger.debug('Registering OpenTelemetry LLM observability tools...'); + + server.tool( + 'capture_llm_observability_opentelemetry', + `Captures LLM usage using OpenTelemetry for observability, including requests, responses, and performance metrics. Works with any OpenTelemetry-compatible backend like Jaeger, New Relic, Grafana, etc.`, + OpenTelemetryLlmInputSchema.shape, + captureOpenTelemetryLlmObservability, + ); + + methodLogger.debug( + 'Successfully registered capture_llm_observability_opentelemetry tool.', + ); +} + +export default { registerTools }; diff --git a/src/tools/posthog-llm.tool.ts b/src/tools/posthog-llm.tool.ts index 1e5bb11..ff7e0e2 100644 --- a/src/tools/posthog-llm.tool.ts +++ b/src/tools/posthog-llm.tool.ts @@ -6,7 +6,7 @@ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { GetToolInputSchema, GetToolInputSchemaType, -} from './posthog-llm.types.js'; +} from '../types/posthog-llm.types.js'; /** * @function capturePosthogLlmObservability diff --git a/src/types/opentelemetry-llm.types.ts b/src/types/opentelemetry-llm.types.ts new file mode 100644 index 0000000..c7c5cdb --- /dev/null +++ b/src/types/opentelemetry-llm.types.ts @@ -0,0 +1,168 @@ +import { z } from 'zod'; + +/** + * Zod schema for the OpenTelemetry LLM observability tool arguments + */ +export const OpenTelemetryLlmInputSchema = z.object({ + // Required fields + userId: z.string().describe('The distinct ID of the user'), + model: z.string().describe('The model used (e.g., gpt-4, claude-3, etc.)'), + provider: z + .string() + .describe('The LLM provider (e.g., openai, anthropic, etc.)'), + + // Optional fields + traceId: z.string().optional().describe('The trace ID to group AI events'), + input: z + .any() + .optional() + .describe('The input to the LLM (messages, prompt, etc.)'), + outputChoices: z.any().optional().describe('The output from the LLM'), + inputTokens: z + .number() + .optional() + .describe('The number of tokens in the input'), + outputTokens: z + .number() + .optional() + .describe('The number of tokens in the output'), + latency: z + .number() + .optional() + .describe('The latency of the LLM call in seconds'), + httpStatus: z + .number() + .optional() + .describe('The HTTP status code of the LLM call'), + baseUrl: z.string().optional().describe('The base URL of the LLM API'), + + // OpenTelemetry specific fields + operationName: z + .string() + .optional() + .describe('The name of the operation being performed'), + error: z + .string() + .optional() + .describe('Error message if the request failed'), + errorType: z + .string() + .optional() + .describe('Type of error (e.g., rate_limit, timeout, etc.)'), + mcpToolsUsed: z + .array(z.string()) + .optional() + .describe('List of MCP tools used during the request'), +}); + +/** + * TypeScript type inferred from the OpenTelemetryLlmInputSchema Zod schema + */ +export type OpenTelemetryLlmInputSchemaType = z.infer< + typeof OpenTelemetryLlmInputSchema +>; + +/** + * Configuration for OpenTelemetry exporters + */ +export interface OpenTelemetryExporterConfig { + url: string; + headers?: Record; + timeout?: number; +} + +/** + * Supported OpenTelemetry backends + */ +export type OpenTelemetryBackend = + | 'jaeger' + | 'newrelic' + | 'grafana' + | 'datadog' + | 'honeycomb' + | 'lightstep' + | 'custom'; + +/** + * Pre-configured settings for popular OpenTelemetry backends + */ +export const OpenTelemetryBackendConfigs: Record< + OpenTelemetryBackend, + Partial +> = { + jaeger: { + url: 'http://localhost:4318/v1/traces', + headers: {}, + }, + newrelic: { + url: 'https://otlp.nr-data.net:4318/v1/traces', + headers: { + 'api-key': process.env.NEW_RELIC_LICENSE_KEY || '', + }, + }, + grafana: { + url: 'https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces', + headers: { + Authorization: + process.env.GRAFANA_INSTANCE_ID && process.env.GRAFANA_API_KEY + ? `Basic ${Buffer.from( + `${process.env.GRAFANA_INSTANCE_ID}:${process.env.GRAFANA_API_KEY}`, + ).toString('base64')}` + : '', + }, + }, + datadog: { + url: 'https://api.datadoghq.com/api/v2/series', + headers: { + 'DD-API-KEY': process.env.DD_API_KEY || '', + }, + }, + honeycomb: { + url: 'https://api.honeycomb.io/v1/traces', + headers: { + 'x-honeycomb-team': process.env.HONEYCOMB_API_KEY || '', + }, + }, + lightstep: { + url: 'https://ingest.lightstep.com:443/api/v2/otel/trace', + headers: { + 'lightstep-access-token': process.env.LIGHTSTEP_ACCESS_TOKEN || '', + }, + }, + custom: { + url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318', + headers: {}, + }, +}; + +/** + * Metric names used by the OpenTelemetry service + */ +export const OpenTelemetryMetrics = { + REQUESTS_TOTAL: 'llm.requests.total', + TOKENS_TOTAL: 'llm.tokens.total', + LATENCY_DURATION: 'llm.latency.duration', + REQUESTS_ACTIVE: 'llm.requests.active', +} as const; + +/** + * Trace attribute names used by the OpenTelemetry service + */ +export const OpenTelemetryAttributes = { + LLM_MODEL: 'llm.model', + LLM_PROVIDER: 'llm.provider', + LLM_USER_ID: 'llm.user_id', + LLM_OPERATION: 'llm.operation', + LLM_INPUT_TOKENS: 'llm.input_tokens', + LLM_OUTPUT_TOKENS: 'llm.output_tokens', + LLM_TOTAL_TOKENS: 'llm.total_tokens', + LLM_LATENCY_MS: 'llm.latency_ms', + LLM_HTTP_STATUS: 'llm.http_status', + LLM_BASE_URL: 'llm.base_url', + LLM_ERROR: 'llm.error', + LLM_ERROR_TYPE: 'llm.error_type', + LLM_INPUT: 'llm.input', + LLM_OUTPUT: 'llm.output', + LLM_MCP_TOOLS_USED: 'llm.mcp_tools_used', + TRACE_ID: 'trace.id', +} as const; diff --git a/src/tools/posthog-llm.types.ts b/src/types/posthog-llm.types.ts similarity index 100% rename from src/tools/posthog-llm.types.ts rename to src/types/posthog-llm.types.ts