Refactor config loading (in progress 3)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Command } from 'commander';
|
import { Command } from 'commander';
|
||||||
import { Logger } from '../utils/logger.util.js';
|
import { Logger } from '../utils/logger.util.js';
|
||||||
import { handleCliError } from '../utils/error.util.js';
|
import { handleCliError } from '../utils/error.util.js';
|
||||||
import postHogLlmController from '../controllers/posthog-llm.controller.js';
|
//import { PostHogController } from '../controllers/posthog-llm.controller.js';
|
||||||
|
|
||||||
const logger = Logger.forContext('cli/posthog-llm.cli.ts');
|
const logger = Logger.forContext('cli/posthog-llm.cli.ts');
|
||||||
|
|
||||||
@@ -48,7 +48,9 @@ function register(program: Command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const args = { eventName, distinctId, properties };
|
const args = { eventName, distinctId, properties };
|
||||||
await postHogLlmController.capture(args);
|
actionLogger.debug(`CLI posthog-llm capture args`, args);
|
||||||
|
// TODO: Implement the controller capture with correct properties
|
||||||
|
//await PostHogController.capture(args);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleCliError(error);
|
handleCliError(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
import { config } from './global.config.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base configuration class with common functionality
|
|
||||||
*/
|
|
||||||
export abstract class BaseConfig<T> {
|
|
||||||
protected abstract schema: z.ZodSchema<T>;
|
|
||||||
protected abstract configKey: string;
|
|
||||||
|
|
||||||
protected getValue(key: string, defaultValue?: string): string | undefined {
|
|
||||||
return config.get(key, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getBoolean(key: string, defaultValue: boolean = false): boolean {
|
|
||||||
return config.getBoolean(key, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getNumber(key: string, defaultValue: number): number {
|
|
||||||
const value = this.getValue(key);
|
|
||||||
if (value === undefined) return defaultValue;
|
|
||||||
return Number(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected parseHeaders(headersString: string): Record<string, string> {
|
|
||||||
const headers: Record<string, string> = {};
|
|
||||||
const pairs = headersString.split(',');
|
|
||||||
for (const pair of pairs) {
|
|
||||||
const [key, value] = pair.split('=');
|
|
||||||
if (key && value) {
|
|
||||||
headers[key.trim()] = value.trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public load(): T {
|
|
||||||
return this.schema.parse(this.loadFromSources());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract loadFromSources(): Record<string, unknown>;
|
|
||||||
}
|
|
||||||
14
src/config/common.schema.ts
Normal file
14
src/config/common.schema.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common/shared configuration options schema (now includes global options)
|
||||||
|
*/
|
||||||
|
export const CommonConfigSchema = z.object({
|
||||||
|
serviceName: z.string().default('llm-observability-mcp'),
|
||||||
|
serviceVersion: z.string().default('1.0.0'),
|
||||||
|
environment: z.string().default('development'),
|
||||||
|
debug: z.boolean().default(false),
|
||||||
|
logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CommonConfigType = z.infer<typeof CommonConfigSchema>;
|
||||||
202
src/config/config-loader.ts
Normal file
202
src/config/config-loader.ts
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { Logger } from '../utils/logger.util.js';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import os from 'os';
|
||||||
|
import { CommonConfigSchema, CommonConfigType } from './common.schema.js';
|
||||||
|
import { PostHogConfigSchema, PostHogConfigType } from './posthog.schema.js';
|
||||||
|
import {
|
||||||
|
OpenTelemetryConfigSchema,
|
||||||
|
OpenTelemetryConfigType,
|
||||||
|
} from './opentelemetry-llm.schema.js';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration loader that handles multiple sources with priority:
|
||||||
|
* 1. Direct ENV pass (process.env)
|
||||||
|
* 2. .env file in project root
|
||||||
|
* 3. Global config file at $HOME/.mcp/configs.json
|
||||||
|
*/
|
||||||
|
class ConfigLoader {
|
||||||
|
private packageName: string;
|
||||||
|
private configLoaded: boolean = false;
|
||||||
|
|
||||||
|
constructor(packageName: string) {
|
||||||
|
this.packageName = packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
load(): void {
|
||||||
|
const logger = Logger.forContext('config-loader', 'load');
|
||||||
|
|
||||||
|
if (this.configLoaded) {
|
||||||
|
logger.debug('Configuration already loaded, skipping');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Loading configuration...');
|
||||||
|
|
||||||
|
// Load from global config file
|
||||||
|
this.loadFromGlobalConfig();
|
||||||
|
|
||||||
|
// Load from .env file
|
||||||
|
this.loadFromEnvFile();
|
||||||
|
|
||||||
|
this.configLoaded = true;
|
||||||
|
logger.debug('Configuration loaded successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadFromEnvFile(): void {
|
||||||
|
const logger = Logger.forContext('config-loader', 'loadFromEnvFile');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = dotenv.config();
|
||||||
|
if (result.error) {
|
||||||
|
logger.debug('No .env file found or error reading it');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.debug('Loaded configuration from .env file');
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error loading .env file', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadFromGlobalConfig(): void {
|
||||||
|
const logger = Logger.forContext(
|
||||||
|
'config-loader',
|
||||||
|
'loadFromGlobalConfig',
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const homedir = os.homedir();
|
||||||
|
const globalConfigPath = path.join(homedir, '.mcp', 'configs.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(globalConfigPath)) {
|
||||||
|
logger.debug('Global config file not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configContent = fs.readFileSync(globalConfigPath, 'utf8');
|
||||||
|
const config = JSON.parse(configContent);
|
||||||
|
|
||||||
|
const shortKey = 'llm-observability-mcp';
|
||||||
|
const fullPackageName = this.packageName;
|
||||||
|
const unscopedPackageName =
|
||||||
|
fullPackageName.split('/')[1] || fullPackageName;
|
||||||
|
|
||||||
|
const potentialKeys = [
|
||||||
|
shortKey,
|
||||||
|
fullPackageName,
|
||||||
|
unscopedPackageName,
|
||||||
|
];
|
||||||
|
let foundConfig = null;
|
||||||
|
|
||||||
|
for (const key of potentialKeys) {
|
||||||
|
if (
|
||||||
|
config[key] &&
|
||||||
|
typeof config[key] === 'object' &&
|
||||||
|
config[key].environments
|
||||||
|
) {
|
||||||
|
foundConfig = config[key];
|
||||||
|
logger.debug(`Found configuration using key: ${key}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundConfig || !foundConfig.environments) {
|
||||||
|
logger.debug(
|
||||||
|
`No config found for ${this.packageName} using keys: ${potentialKeys.join(', ')}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(
|
||||||
|
foundConfig.environments,
|
||||||
|
)) {
|
||||||
|
if (process.env[key] === undefined) {
|
||||||
|
process.env[key] = String(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`Loaded configuration from global config file`);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error loading global config file', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getEnvObject(schema: z.ZodTypeAny): Record<string, unknown> {
|
||||||
|
const shape =
|
||||||
|
typeof schema._def.shape === 'function'
|
||||||
|
? schema._def.shape()
|
||||||
|
: schema._def.shape;
|
||||||
|
const result: Record<string, unknown> = {};
|
||||||
|
for (const key in shape) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(shape, key)) {
|
||||||
|
const envKey = this.toEnvKey(key);
|
||||||
|
const value = process.env[envKey];
|
||||||
|
if (value !== undefined) {
|
||||||
|
const type = shape[key]._def.typeName;
|
||||||
|
if (type === 'ZodNumber') {
|
||||||
|
result[key] = Number(value);
|
||||||
|
} else if (type === 'ZodBoolean') {
|
||||||
|
result[key] = value.toLowerCase() === 'true';
|
||||||
|
} else {
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private toEnvKey(key: string): string {
|
||||||
|
// Map schema keys to environment variable names
|
||||||
|
// e.g. serviceName -> OTEL_SERVICE_NAME, apiKey -> POSTHOG_API_KEY, etc.
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
serviceName: 'OTEL_SERVICE_NAME',
|
||||||
|
serviceVersion: 'OTEL_SERVICE_VERSION',
|
||||||
|
environment: 'OTEL_ENVIRONMENT',
|
||||||
|
metricsEndpoint: 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT',
|
||||||
|
tracesEndpoint: 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT',
|
||||||
|
logsEndpoint: 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT',
|
||||||
|
headers: 'OTEL_EXPORTER_OTLP_HEADERS',
|
||||||
|
exportIntervalMillis: 'OTEL_METRIC_EXPORT_INTERVAL',
|
||||||
|
exportTimeoutMillis: 'OTEL_METRIC_EXPORT_TIMEOUT',
|
||||||
|
samplingRatio: 'OTEL_TRACES_SAMPLER_ARG',
|
||||||
|
apiKey: 'POSTHOG_API_KEY',
|
||||||
|
host: 'POSTHOG_HOST',
|
||||||
|
debug: 'DEBUG',
|
||||||
|
logLevel: 'LOG_LEVEL',
|
||||||
|
};
|
||||||
|
return map[key] || key;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommonConfig(): CommonConfigType {
|
||||||
|
return CommonConfigSchema.parse(this.getEnvObject(CommonConfigSchema));
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosthogConfig(): PostHogConfigType {
|
||||||
|
return PostHogConfigSchema.parse(
|
||||||
|
this.getEnvObject(PostHogConfigSchema),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOpenTelemetryConfig(): OpenTelemetryConfigType {
|
||||||
|
const raw = this.getEnvObject(OpenTelemetryConfigSchema);
|
||||||
|
// Special handling for headers (parse comma-separated string to object)
|
||||||
|
if (typeof raw.headers === 'string') {
|
||||||
|
const headers: Record<string, string> = {};
|
||||||
|
const pairs = raw.headers.split(',');
|
||||||
|
for (const pair of pairs) {
|
||||||
|
const [key, value] = pair.split('=');
|
||||||
|
if (key && value) {
|
||||||
|
headers[key.trim()] = value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raw.headers = headers;
|
||||||
|
}
|
||||||
|
return OpenTelemetryConfigSchema.parse(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const configLoader = new ConfigLoader('@sfiorini/llm-observability-mcp');
|
||||||
|
export default configLoader;
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import { Logger } from '../utils/logger.util.js';
|
|
||||||
import dotenv from 'dotenv';
|
|
||||||
import os from 'os';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration loader that handles multiple sources with priority:
|
|
||||||
* 1. Direct ENV pass (process.env)
|
|
||||||
* 2. .env file in project root
|
|
||||||
* 3. Global config file at $HOME/.mcp/configs.json
|
|
||||||
*/
|
|
||||||
class ConfigLoader {
|
|
||||||
private packageName: string;
|
|
||||||
private configLoaded: boolean = false;
|
|
||||||
|
|
||||||
constructor(packageName: string) {
|
|
||||||
this.packageName = packageName;
|
|
||||||
}
|
|
||||||
|
|
||||||
load(): void {
|
|
||||||
const logger = Logger.forContext('global.config', 'load');
|
|
||||||
|
|
||||||
if (this.configLoaded) {
|
|
||||||
logger.debug('Configuration already loaded, skipping');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug('Loading configuration...');
|
|
||||||
|
|
||||||
// Load from global config file
|
|
||||||
this.loadFromGlobalConfig();
|
|
||||||
|
|
||||||
// Load from .env file
|
|
||||||
this.loadFromEnvFile();
|
|
||||||
|
|
||||||
this.configLoaded = true;
|
|
||||||
logger.debug('Configuration loaded successfully');
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadFromEnvFile(): void {
|
|
||||||
const logger = Logger.forContext('global.config', 'loadFromEnvFile');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = dotenv.config();
|
|
||||||
if (result.error) {
|
|
||||||
logger.debug('No .env file found or error reading it');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.debug('Loaded configuration from .env file');
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error loading .env极 file', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadFromGlobalConfig(): void {
|
|
||||||
const logger = Logger.forContext(
|
|
||||||
'global.config',
|
|
||||||
'loadFromGlobalConfig',
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const homedir = os.homedir();
|
|
||||||
const globalConfigPath = path.join(homedir, '.mcp', 'configs.json');
|
|
||||||
|
|
||||||
if (!fs.existsSync(globalConfigPath)) {
|
|
||||||
logger.debug('Global config file not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const configContent = fs.readFileSync(globalConfigPath, 'utf8');
|
|
||||||
const config = JSON.parse(configContent);
|
|
||||||
|
|
||||||
const shortKey = 'llm-observability-mcp';
|
|
||||||
const fullPackageName = this.packageName;
|
|
||||||
const unscopedPackageName =
|
|
||||||
fullPackageName.split('/')[1] || fullPackageName;
|
|
||||||
|
|
||||||
const potentialKeys = [
|
|
||||||
shortKey,
|
|
||||||
fullPackageName,
|
|
||||||
unscopedPackageName,
|
|
||||||
];
|
|
||||||
let foundConfig = null;
|
|
||||||
|
|
||||||
for (const key of potentialKeys) {
|
|
||||||
if (
|
|
||||||
config[key] &&
|
|
||||||
typeof config[key] === 'object' &&
|
|
||||||
config[key].environments
|
|
||||||
) {
|
|
||||||
foundConfig = config[key];
|
|
||||||
logger.debug(`Found configuration using key: ${key}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundConfig || !foundConfig.environments) {
|
|
||||||
logger.debug(
|
|
||||||
`No config found for ${this.packageName} using keys: ${potentialKeys.join(', ')}`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(
|
|
||||||
foundConfig.environments,
|
|
||||||
)) {
|
|
||||||
if (process.env[key] === undefined) {
|
|
||||||
process.env[key] = String(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(`Loaded configuration from global config file`);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error loading global config file', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get(key: string, defaultValue?: string): string | undefined {
|
|
||||||
return process.env[key] || defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBoolean(key: string, defaultValue: boolean = false): boolean {
|
|
||||||
const value = this.get(key);
|
|
||||||
if (value === undefined) return defaultValue;
|
|
||||||
return value.toLowerCase() === 'true';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config = new ConfigLoader('@sfiorini/llm-observability-mcp');
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import { BaseConfig } from './base.config.js';
|
|
||||||
import {
|
|
||||||
OpenTelemetryConfigSchema,
|
|
||||||
type OpenTelemetryConfigType,
|
|
||||||
} from './opentelemetry-llm.schema.js';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OpenTelemetry configuration class
|
|
||||||
*/
|
|
||||||
export class OpenTelemetryConfig extends BaseConfig<OpenTelemetryConfigType> {
|
|
||||||
protected schema =
|
|
||||||
OpenTelemetryConfigSchema as z.ZodSchema<OpenTelemetryConfigType>;
|
|
||||||
protected configKey = 'opentelemetry';
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create configuration from environment variables
|
|
||||||
*/
|
|
||||||
public fromEnv(): OpenTelemetryConfigType {
|
|
||||||
return super.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if OpenTelemetry is enabled
|
|
||||||
*/
|
|
||||||
public isEnabled(config: OpenTelemetryConfigType): boolean {
|
|
||||||
return !!(
|
|
||||||
config.metricsEndpoint ||
|
|
||||||
config.tracesEndpoint ||
|
|
||||||
config.logsEndpoint
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get combined OTLP endpoint if only one is provided
|
|
||||||
*/
|
|
||||||
public getDefaultEndpoint(
|
|
||||||
config: OpenTelemetryConfigType,
|
|
||||||
): string | undefined {
|
|
||||||
if (
|
|
||||||
config.metricsEndpoint &&
|
|
||||||
config.tracesEndpoint === config.metricsEndpoint
|
|
||||||
) {
|
|
||||||
return config.metricsEndpoint;
|
|
||||||
}
|
|
||||||
return config.metricsEndpoint || config.tracesEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected loadFromSources(): Record<string, unknown> {
|
|
||||||
return {
|
|
||||||
serviceName: this.getValue(
|
|
||||||
'OTEL_SERVICE_NAME',
|
|
||||||
'llm-observability-mcp',
|
|
||||||
),
|
|
||||||
serviceVersion: this.getValue('OTEL_SERVICE_VERSION', '1.0.0'),
|
|
||||||
environment: this.getValue('OTEL_ENVIRONMENT', 'development'),
|
|
||||||
metricsEndpoint: this.getValue(
|
|
||||||
'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT',
|
|
||||||
),
|
|
||||||
tracesEndpoint: this.getValue('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT'),
|
|
||||||
logsEndpoint: this.getValue('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT'),
|
|
||||||
headers: this.getValue('OTEL_EXPORTER_OTLP_HEADERS')
|
|
||||||
? this.parseHeaders(
|
|
||||||
this.getValue('OTEL_EXPORTER_OTLP_HEADERS')!,
|
|
||||||
)
|
|
||||||
: undefined,
|
|
||||||
exportIntervalMillis: this.getNumber(
|
|
||||||
'OTEL_METRIC_EXPORT_INTERVAL',
|
|
||||||
10000,
|
|
||||||
),
|
|
||||||
exportTimeoutMillis: this.getNumber(
|
|
||||||
'OTEL_METRIC_EXPORT_TIMEOUT',
|
|
||||||
5000,
|
|
||||||
),
|
|
||||||
samplingRatio: this.getNumber('OTEL_TRACES_SAMPLER_ARG', 1.0),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import { CommonConfigSchema } from './common.schema.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration schema for OpenTelemetry
|
* Configuration schema for OpenTelemetry
|
||||||
*/
|
*/
|
||||||
export const OpenTelemetryConfigSchema = z.object({
|
export const OpenTelemetryConfigSchema = CommonConfigSchema.extend({
|
||||||
// Service configuration
|
|
||||||
serviceName: z.string().default('llm-observability-mcp'),
|
|
||||||
serviceVersion: z.string().default('1.0.0'),
|
|
||||||
environment: z.string().default('development'),
|
|
||||||
|
|
||||||
// OTLP endpoints
|
// OTLP endpoints
|
||||||
metricsEndpoint: z.string().optional(),
|
metricsEndpoint: z.string().optional(),
|
||||||
tracesEndpoint: z.string().optional(),
|
tracesEndpoint: z.string().optional(),
|
||||||
|
|||||||
15
src/config/posthog.schema.ts
Normal file
15
src/config/posthog.schema.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { CommonConfigSchema } from './common.schema.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PostHog configuration options schema
|
||||||
|
*/
|
||||||
|
export const PostHogConfigSchema = CommonConfigSchema.extend({
|
||||||
|
apiKey: z.string().describe('PostHog API key'),
|
||||||
|
host: z
|
||||||
|
.string()
|
||||||
|
.default('https://app.posthog.com')
|
||||||
|
.describe('PostHog host URL'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PostHogConfigType = z.infer<typeof PostHogConfigSchema>;
|
||||||
8
src/controllers/base.controller.ts
Normal file
8
src/controllers/base.controller.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { ControllerResponse } from '../types/common.types.js';
|
||||||
|
|
||||||
|
export abstract class BaseController {
|
||||||
|
// Instance method (must be implemented by subclasses)
|
||||||
|
static capture(data: unknown): Promise<ControllerResponse> {
|
||||||
|
throw new Error(`Not implemented: capture: ${data}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Logger } from '../utils/logger.util.js';
|
import { Logger } from '../utils/logger.util.js';
|
||||||
|
import { BaseController } from './base.controller.js';
|
||||||
import { OpenTelemetryService } from '../services/opentelemetry-llm.service.js';
|
import { OpenTelemetryService } from '../services/opentelemetry-llm.service.js';
|
||||||
import { OpenTelemetryLlmInputSchemaType } from '../types/opentelemetry-llm.types.js';
|
import { OpenTelemetryLlmInputSchemaType } from '../types/opentelemetry-llm.types.js';
|
||||||
import { ControllerResponse } from '../types/common.types.js';
|
import { ControllerResponse } from '../types/common.types.js';
|
||||||
@@ -6,17 +7,11 @@ import { ControllerResponse } from '../types/common.types.js';
|
|||||||
/**
|
/**
|
||||||
* Controller for OpenTelemetry LLM observability
|
* Controller for OpenTelemetry LLM observability
|
||||||
*/
|
*/
|
||||||
export class OpenTelemetryController {
|
export class OpenTelemetryController extends BaseController {
|
||||||
private service: OpenTelemetryService;
|
|
||||||
|
|
||||||
constructor(service: OpenTelemetryService) {
|
|
||||||
this.service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture LLM observability data
|
* Capture LLM observability data
|
||||||
*/
|
*/
|
||||||
public async captureLlmObservability(
|
static async capture(
|
||||||
data: OpenTelemetryLlmInputSchemaType,
|
data: OpenTelemetryLlmInputSchemaType,
|
||||||
): Promise<ControllerResponse> {
|
): Promise<ControllerResponse> {
|
||||||
const logger = Logger.forContext(
|
const logger = Logger.forContext(
|
||||||
@@ -26,8 +21,12 @@ export class OpenTelemetryController {
|
|||||||
logger.debug('Capturing LLM observability data', data);
|
logger.debug('Capturing LLM observability data', data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Get and initialize the service
|
||||||
|
const service = OpenTelemetryService.getInstance();
|
||||||
|
service.initialize();
|
||||||
|
|
||||||
// Record the LLM request
|
// Record the LLM request
|
||||||
this.service.recordLlmRequest(data);
|
service.recordLlmRequest(data);
|
||||||
|
|
||||||
const content = `## OpenTelemetry LLM Observability Captured
|
const content = `## OpenTelemetry LLM Observability Captured
|
||||||
|
|
||||||
@@ -64,11 +63,3 @@ The data has been sent to your configured OpenTelemetry collector and is now ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export singleton instance
|
|
||||||
const openTelemetryService = OpenTelemetryService.getInstance();
|
|
||||||
const openTelemetryController = new OpenTelemetryController(
|
|
||||||
openTelemetryService,
|
|
||||||
);
|
|
||||||
|
|
||||||
export default openTelemetryController;
|
|
||||||
|
|||||||
@@ -1,32 +1,35 @@
|
|||||||
import { Logger } from '../utils/logger.util.js';
|
import { Logger } from '../utils/logger.util.js';
|
||||||
import { ControllerResponse } from '../types/common.types.js';
|
import { ControllerResponse } from '../types/common.types.js';
|
||||||
import postHogLlmService from '../services/posthog-llm.service.js';
|
import postHogLlmService from '../services/posthog-llm.service.js';
|
||||||
|
import { PostHogLlmInputSchemaType } from '../types/posthog-llm.types.js';
|
||||||
|
|
||||||
const logger = Logger.forContext('controllers/posthog-llm.controller.ts');
|
/**
|
||||||
|
* Controller for PostHog LLM observability
|
||||||
|
*/
|
||||||
|
export class PostHogController {
|
||||||
|
/**
|
||||||
|
* Capture LLM observability data
|
||||||
|
*/
|
||||||
|
|
||||||
async function capture(args: {
|
static async capture(
|
||||||
eventName: string;
|
data: PostHogLlmInputSchemaType,
|
||||||
distinctId: string;
|
): Promise<ControllerResponse> {
|
||||||
properties: Record<string, unknown>;
|
const logger = Logger.forContext('posthog-llm.controller', 'capture');
|
||||||
}): Promise<ControllerResponse> {
|
logger.debug('Capturing LLM observability data', data);
|
||||||
const methodLogger = logger.forMethod('capture');
|
|
||||||
methodLogger.debug('Capturing PostHog event...');
|
|
||||||
methodLogger.debug('Arguments:', args);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await postHogLlmService.capture(args);
|
await postHogLlmService.capture(data);
|
||||||
return {
|
return {
|
||||||
content: 'Event captured successfully.',
|
content: 'Event captured successfully.',
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
methodLogger.error('Error capturing event:', error);
|
logger.error('Error capturing event:', error);
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
error instanceof Error ? error.message : 'Unknown error';
|
error instanceof Error ? error.message : 'Unknown error';
|
||||||
methodLogger.error('Error capturing event:', errorMessage);
|
logger.error('Error capturing event:', errorMessage);
|
||||||
return {
|
return {
|
||||||
content: `Failed to capture event: ${errorMessage}`,
|
content: `Failed to capture event: ${errorMessage}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { capture };
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
import { Logger } from './utils/logger.util.js';
|
import { Logger } from './utils/logger.util.js';
|
||||||
import { config } from './config/global.config.js';
|
import configLoader from './config/config-loader.js';
|
||||||
import { runCli } from './cli/index.js';
|
import { runCli } from './cli/index.js';
|
||||||
import { stdioTransport } from './server/stdio.js';
|
import { stdioTransport } from './server/stdio.js';
|
||||||
import { streamableHttpTransport } from './server/streamableHttp.js';
|
import { streamableHttpTransport } from './server/streamableHttp.js';
|
||||||
@@ -46,7 +46,7 @@ async function main() {
|
|||||||
const mainLogger = Logger.forContext('index.ts', 'main');
|
const mainLogger = Logger.forContext('index.ts', 'main');
|
||||||
|
|
||||||
// Load configuration
|
// Load configuration
|
||||||
config.load();
|
configLoader.load();
|
||||||
|
|
||||||
// CLI mode - if any arguments are provided
|
// CLI mode - if any arguments are provided
|
||||||
if (process.argv.length > 2) {
|
if (process.argv.length > 2) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import { Logger } from '../utils/logger.util.js';
|
import { Logger } from '../utils/logger.util.js';
|
||||||
import posthogLlmController from '../controllers/posthog-llm.controller.js';
|
import { PostHogController } from '../controllers/posthog-llm.controller.js';
|
||||||
import { formatErrorForMcpResource } from '../utils/error.util.js';
|
import { formatErrorForMcpResource } from '../utils/error.util.js';
|
||||||
|
|
||||||
const logger = Logger.forContext('resources/posthog-llm.resource.ts');
|
const logger = Logger.forContext('resources/posthog-llm.resource.ts');
|
||||||
@@ -44,7 +44,8 @@ function registerResources(server: McpServer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call the controller to capture the event
|
// Call the controller to capture the event
|
||||||
const result = await posthogLlmController.capture({
|
// TODO: Implement the controller capture with correct properties
|
||||||
|
const result = await PostHogController.capture({
|
||||||
eventName,
|
eventName,
|
||||||
distinctId,
|
distinctId,
|
||||||
properties,
|
properties,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|||||||
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||||
|
|
||||||
import { Logger } from '../utils/logger.util';
|
import { Logger } from '../utils/logger.util';
|
||||||
import { config } from '../config/global.config';
|
import configLoader from '../config/config-loader';
|
||||||
import { PACKAGE_NAME, VERSION } from '../utils/constants.util';
|
import { PACKAGE_NAME, VERSION } from '../utils/constants.util';
|
||||||
import posthogLlmResources from '../resources/posthog-llm.resource.js';
|
import posthogLlmResources from '../resources/posthog-llm.resource.js';
|
||||||
import posthogLlmTools from '../tools/posthog-llm.tool.js';
|
import posthogLlmTools from '../tools/posthog-llm.tool.js';
|
||||||
@@ -14,9 +14,10 @@ export function createServer() {
|
|||||||
|
|
||||||
// Load configuration
|
// Load configuration
|
||||||
serverLogger.info('Starting MCP server initialization...');
|
serverLogger.info('Starting MCP server initialization...');
|
||||||
config.load();
|
configLoader.load();
|
||||||
|
|
||||||
if (config.getBoolean('DEBUG')) {
|
const commonConfig = configLoader.getCommonConfig();
|
||||||
|
if (commonConfig.debug) {
|
||||||
serverLogger.debug('Debug mode enabled');
|
serverLogger.debug('Debug mode enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
metrics,
|
metrics,
|
||||||
} from '@opentelemetry/api';
|
} from '@opentelemetry/api';
|
||||||
import { trace, Tracer, SpanStatusCode } from '@opentelemetry/api';
|
import { trace, Tracer, SpanStatusCode } from '@opentelemetry/api';
|
||||||
|
import configLoader from '../config/config-loader';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenTelemetry service for LLM observability
|
* OpenTelemetry service for LLM observability
|
||||||
@@ -41,8 +42,8 @@ export class OpenTelemetryService {
|
|||||||
private latencyHistogram: Histogram;
|
private latencyHistogram: Histogram;
|
||||||
private activeRequests: UpDownCounter;
|
private activeRequests: UpDownCounter;
|
||||||
|
|
||||||
private constructor(config: OpenTelemetryConfigType) {
|
private constructor() {
|
||||||
this.config = config;
|
this.config = configLoader.getOpenTelemetryConfig();
|
||||||
this.tracer = trace.getTracer('llm-observability-mcp');
|
this.tracer = trace.getTracer('llm-observability-mcp');
|
||||||
this.meter = this.initializeMetrics();
|
this.meter = this.initializeMetrics();
|
||||||
|
|
||||||
@@ -74,16 +75,9 @@ export class OpenTelemetryService {
|
|||||||
/**
|
/**
|
||||||
* Get singleton instance
|
* Get singleton instance
|
||||||
*/
|
*/
|
||||||
public static getInstance(
|
public static getInstance(): OpenTelemetryService {
|
||||||
config?: OpenTelemetryConfigType,
|
|
||||||
): OpenTelemetryService {
|
|
||||||
if (!OpenTelemetryService.instance) {
|
if (!OpenTelemetryService.instance) {
|
||||||
if (!config) {
|
OpenTelemetryService.instance = new OpenTelemetryService();
|
||||||
throw new Error(
|
|
||||||
'OpenTelemetryService requires configuration on first initialization',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
OpenTelemetryService.instance = new OpenTelemetryService(config);
|
|
||||||
}
|
}
|
||||||
return OpenTelemetryService.instance;
|
return OpenTelemetryService.instance;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Logger } from '../utils/logger.util';
|
import { Logger } from '../utils/logger.util';
|
||||||
import { PostHog } from 'posthog-node';
|
import { PostHog } from 'posthog-node';
|
||||||
import { config } from '../config/global.config';
|
import configLoader from '../config/config-loader';
|
||||||
|
|
||||||
// Ensure configuration is loaded before accessing environment variables
|
// Ensure configuration is loaded before accessing environment variables
|
||||||
config.load();
|
configLoader.load();
|
||||||
|
|
||||||
const logger = Logger.forContext('services/posthog-llm.service.ts');
|
const logger = Logger.forContext('services/posthog-llm.service.ts');
|
||||||
|
|
||||||
const posthogApiKey = config.get('POSTHOG_API_KEY');
|
const posthogConfig = configLoader.getPosthogConfig();
|
||||||
let posthogClient: PostHog | null = null;
|
let posthogClient: PostHog | null = null;
|
||||||
|
|
||||||
if (posthogApiKey) {
|
if (posthogConfig.apiKey) {
|
||||||
posthogClient = new PostHog(posthogApiKey, {
|
posthogClient = new PostHog(posthogConfig.apiKey, {
|
||||||
host: config.get('POSTHOG_HOST') || 'https://app.posthog.com',
|
host: posthogConfig.host,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.warn('POSTHOG_API_KEY is not set. PostHog client not initialized.');
|
logger.warn('POSTHOG_API_KEY is not set. PostHog client not initialized.');
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import { Logger } from '../utils/logger.util.js';
|
import { Logger } from '../utils/logger.util.js';
|
||||||
import { formatErrorForMcpTool } from '../utils/error.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 { OpenTelemetryController } from '../controllers/opentelemetry-llm.controller.js';
|
||||||
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
||||||
import {
|
import {
|
||||||
OpenTelemetryLlmInputSchema,
|
OpenTelemetryLlmInputSchema,
|
||||||
OpenTelemetryLlmInputSchemaType,
|
OpenTelemetryLlmInputSchemaType,
|
||||||
} from '../types/opentelemetry-llm.types.js';
|
} from '../types/opentelemetry-llm.types.js';
|
||||||
import { OpenTelemetryConfig } from '../config/opentelemetry-llm.config.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function captureOpenTelemetryLlmObservability
|
* @function captureOpenTelemetryLlmObservability
|
||||||
@@ -31,19 +29,8 @@ async function captureOpenTelemetryLlmObservability(
|
|||||||
// Parse and validate arguments
|
// Parse and validate arguments
|
||||||
const validatedArgs = OpenTelemetryLlmInputSchema.parse(args);
|
const validatedArgs = OpenTelemetryLlmInputSchema.parse(args);
|
||||||
|
|
||||||
// Ensure OpenTelemetry is configured
|
|
||||||
const configLoader = new OpenTelemetryConfig();
|
|
||||||
const config = configLoader.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
|
// Pass validated args to the controller
|
||||||
const result = await controller.captureLlmObservability(validatedArgs);
|
const result = await OpenTelemetryController.capture(validatedArgs);
|
||||||
methodLogger.debug('Got response from controller', result);
|
methodLogger.debug('Got response from controller', result);
|
||||||
|
|
||||||
// Format the response for the MCP tool
|
// Format the response for the MCP tool
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import { Logger } from '../utils/logger.util.js';
|
import { Logger } from '../utils/logger.util.js';
|
||||||
import { formatErrorForMcpTool } from '../utils/error.util.js';
|
import { formatErrorForMcpTool } from '../utils/error.util.js';
|
||||||
import posthogLlmController from '../controllers/posthog-llm.controller.js';
|
import { PostHogController } from '../controllers/posthog-llm.controller.js';
|
||||||
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
||||||
import {
|
import {
|
||||||
GetToolInputSchema,
|
PostHogLlmInputSchemaType,
|
||||||
GetToolInputSchemaType,
|
PostHogLlmPropertiesApiSchemaType,
|
||||||
|
PostHogLlmPropertiesPayloadSchema,
|
||||||
|
PostHogLlmPropertiesPayloadSchemaType,
|
||||||
} from '../types/posthog-llm.types.js';
|
} from '../types/posthog-llm.types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function capturePosthogLlmObservability
|
* @function capturePosthogLlmObservability
|
||||||
* @description MCP Tool handler to capture LLM observability events in PostHog.
|
* @description MCP Tool handler to capture LLM observability events in PostHog.
|
||||||
* It calls the posthogLlmController to track the data and formats the response for the MCP.
|
* It calls the posthogLlmController to track the data and formats the response for the MCP.
|
||||||
* @param {GetToolInputSchemaType} args - Arguments provided to the tool.
|
* @param {PostHogLlmInputSchemaType} args - Arguments provided to the tool.
|
||||||
* @returns {Promise<CallToolResult>} Formatted response for the MCP.
|
* @returns {Promise<CallToolResult>} Formatted response for the MCP.
|
||||||
* @throws {McpError} Formatted error if the controller or service layer encounters an issue.
|
* @throws {McpError} Formatted error if the controller or service layer encounters an issue.
|
||||||
*/
|
*/
|
||||||
async function capturePosthogLlmObservability(
|
async function capturePosthogLlmObservability(
|
||||||
args: GetToolInputSchemaType,
|
args: PostHogLlmPropertiesPayloadSchemaType,
|
||||||
): Promise<CallToolResult> {
|
): Promise<CallToolResult> {
|
||||||
const methodLogger = Logger.forContext(
|
const methodLogger = Logger.forContext(
|
||||||
'tools/posthog-llm.tool.ts',
|
'tools/posthog-llm.tool.ts',
|
||||||
@@ -26,15 +28,16 @@ async function capturePosthogLlmObservability(
|
|||||||
methodLogger.debug(`Capture LLM Observability in PostHog...`, args);
|
methodLogger.debug(`Capture LLM Observability in PostHog...`, args);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const trackArgs = GetToolInputSchema.parse(args);
|
const trackArgs = PostHogLlmPropertiesPayloadSchema.parse(args);
|
||||||
|
|
||||||
const posthogProperties: Record<string, unknown> = {
|
const posthogProperties: PostHogLlmPropertiesApiSchemaType = {
|
||||||
|
userId: trackArgs.userId,
|
||||||
$ai_model: trackArgs.model,
|
$ai_model: trackArgs.model,
|
||||||
$ai_provider: trackArgs.provider,
|
$ai_provider: trackArgs.provider,
|
||||||
};
|
};
|
||||||
|
|
||||||
const toPostHogKey: Partial<
|
const toPostHogKey: Partial<
|
||||||
Record<keyof GetToolInputSchemaType, string>
|
Record<keyof PostHogLlmPropertiesPayloadSchemaType, string>
|
||||||
> = {
|
> = {
|
||||||
input: '$ai_input',
|
input: '$ai_input',
|
||||||
outputChoices: '$ai_output_choices',
|
outputChoices: '$ai_output_choices',
|
||||||
@@ -47,22 +50,27 @@ async function capturePosthogLlmObservability(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (const key of Object.keys(toPostHogKey) as Array<
|
for (const key of Object.keys(toPostHogKey) as Array<
|
||||||
keyof GetToolInputSchemaType
|
keyof PostHogLlmPropertiesPayloadSchemaType
|
||||||
>) {
|
>) {
|
||||||
if (trackArgs[key] !== undefined) {
|
if (trackArgs[key] !== undefined) {
|
||||||
const posthogKey = toPostHogKey[key];
|
const posthogKey = toPostHogKey[key];
|
||||||
if (posthogKey) {
|
if (posthogKey) {
|
||||||
posthogProperties[posthogKey] = trackArgs[key];
|
// Type assertion to satisfy TS: posthogKey is a key of posthogProperties
|
||||||
|
(posthogProperties as Record<string, unknown>)[posthogKey] =
|
||||||
|
trackArgs[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass validated args to the controller
|
// validated input for the controller
|
||||||
const result = await posthogLlmController.capture({
|
const posthogInput: PostHogLlmInputSchemaType = {
|
||||||
eventName: '$ai_generation',
|
eventName: '$ai_generation',
|
||||||
distinctId: trackArgs.userId,
|
distinctId: trackArgs.userId,
|
||||||
properties: posthogProperties,
|
properties: posthogProperties,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Pass validated args to the controller
|
||||||
|
const result = await PostHogController.capture(posthogInput);
|
||||||
methodLogger.debug(`Got the response from the controller`, result);
|
methodLogger.debug(`Got the response from the controller`, result);
|
||||||
|
|
||||||
// Format the response for the MCP tool
|
// Format the response for the MCP tool
|
||||||
@@ -96,7 +104,7 @@ function registerTools(server: McpServer) {
|
|||||||
server.tool(
|
server.tool(
|
||||||
'capture_llm_observability',
|
'capture_llm_observability',
|
||||||
`Captures LLM usage in PostHog for observability, including requests, responses, and performance metrics`,
|
`Captures LLM usage in PostHog for observability, including requests, responses, and performance metrics`,
|
||||||
GetToolInputSchema.shape,
|
PostHogLlmPropertiesPayloadSchema.shape,
|
||||||
capturePosthogLlmObservability,
|
capturePosthogLlmObservability,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zod schema for the PostHog LLM observability tool arguments.
|
* Zod schema for the PostHog LLM observability tool properties (payload).
|
||||||
*/
|
*/
|
||||||
export const GetToolInputSchema = z.object({
|
export const PostHogLlmPropertiesPayloadSchema = z.object({
|
||||||
userId: z.string().describe('The distinct ID of the user'),
|
userId: z.string().describe('The distinct ID of the user'),
|
||||||
traceId: z.string().optional().describe('The trace ID to group AI events'),
|
traceId: z.string().optional().describe('The trace ID to group AI events'),
|
||||||
model: z.string().describe('The model used (e.g., gpt-4, claude-3, etc.)'),
|
model: z.string().describe('The model used (e.g., gpt-4, claude-3, etc.)'),
|
||||||
@@ -34,8 +34,60 @@ export const GetToolInputSchema = z.object({
|
|||||||
baseUrl: z.string().optional().describe('The base URL of the LLM API'),
|
baseUrl: z.string().optional().describe('The base URL of the LLM API'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type PostHogLlmPropertiesPayloadSchemaType = z.infer<
|
||||||
|
typeof PostHogLlmPropertiesPayloadSchema
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TypeScript type inferred from the GetToolInputSchema Zod schema.
|
* Zod schema for the PostHog LLM observability tool properties (api payload).
|
||||||
* This represents the optional arguments passed to the tool handler and controller.
|
|
||||||
*/
|
*/
|
||||||
export type GetToolInputSchemaType = z.infer<typeof GetToolInputSchema>;
|
export const PostHogLlmPropertiesApiSchema = z.object({
|
||||||
|
userId: z.string().describe('The distinct ID of the user'),
|
||||||
|
$ai_trace_id: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe('The trace ID to group AI events'),
|
||||||
|
$ai_model: z
|
||||||
|
.string()
|
||||||
|
.describe('The model used (e.g., gpt-4, claude-3, etc.)'),
|
||||||
|
$ai_provider: z
|
||||||
|
.string()
|
||||||
|
.describe('The LLM provider (e.g., openai, anthropic, etc.)'),
|
||||||
|
$ai_input: z
|
||||||
|
.any()
|
||||||
|
.optional()
|
||||||
|
.describe('The input to the LLM (messages, prompt, etc.)'),
|
||||||
|
$ai_output_choices: z.any().optional().describe('The output from the LLM'),
|
||||||
|
$ai_input_tokens: z
|
||||||
|
.number()
|
||||||
|
.optional()
|
||||||
|
.describe('The number of tokens in the input'),
|
||||||
|
$ai_output_tokens: z
|
||||||
|
.number()
|
||||||
|
.optional()
|
||||||
|
.describe('The number of tokens in the output'),
|
||||||
|
$ai_latency: z
|
||||||
|
.number()
|
||||||
|
.optional()
|
||||||
|
.describe('The latency of the LLM call in seconds'),
|
||||||
|
$ai_http_status: z
|
||||||
|
.number()
|
||||||
|
.optional()
|
||||||
|
.describe('The HTTP status code of the LLM call'),
|
||||||
|
$ai_base_url: z.string().optional().describe('The base URL of the LLM API'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PostHogLlmPropertiesApiSchemaType = z.infer<
|
||||||
|
typeof PostHogLlmPropertiesApiSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for the PostHog LLM observability tool arguments (top-level).
|
||||||
|
*/
|
||||||
|
export const PostHogLlmInputSchema = z.object({
|
||||||
|
eventName: z.string().describe('The name of the event to capture.'),
|
||||||
|
distinctId: z.string().describe('The distinct ID of the user.'),
|
||||||
|
properties: PostHogLlmPropertiesApiSchema.describe('The event properties.'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PostHogLlmInputSchemaType = z.infer<typeof PostHogLlmInputSchema>;
|
||||||
|
|||||||
Reference in New Issue
Block a user