{"version":3,"file":"datadog.cjs","names":["BaseTracer"],"sources":["../../../../src/experimental/callbacks/handlers/datadog.ts"],"sourcesContent":["import { BaseCallbackHandlerInput } from \"@langchain/core/callbacks/base\";\nimport { BaseTracer, Run } from \"@langchain/core/tracers/base\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { Document } from \"@langchain/core/documents\";\nimport { BaseMessage, isAIMessage } from \"@langchain/core/messages\";\nimport { ChatGeneration } from \"@langchain/core/outputs\";\nimport { KVMap } from \"langsmith/schemas\";\n\nexport type DatadogLLMObsSpanKind =\n  | \"llm\"\n  | \"workflow\"\n  | \"agent\"\n  | \"tool\"\n  | \"task\"\n  | \"embedding\"\n  | \"retrieval\";\n\nexport type DatadogLLMObsIO =\n  | { value: string }\n  | {\n      documents: {\n        text?: string;\n        id?: string;\n        name?: string;\n        score: string | number;\n      }[];\n    }\n  | { messages: { content: string; role?: string }[] };\n\nexport interface DatadogLLMObsSpan {\n  span_id: string;\n  trace_id: string;\n  parent_id: string;\n  session_id?: string;\n  name: string;\n  start_ns: number;\n  duration: number;\n  error: number;\n  status: string;\n  tags?: string[];\n  meta: {\n    kind: DatadogLLMObsSpanKind;\n    model_name?: string;\n    model_provider?: string;\n    temperature?: string;\n    input: DatadogLLMObsIO;\n    output: DatadogLLMObsIO | undefined;\n  };\n  metrics: { [key: string]: number };\n}\n\nexport interface DatadogLLMObsRequestBody {\n  data: {\n    type: \"span\";\n    attributes: {\n      ml_app: string;\n      tags: string[];\n      spans: DatadogLLMObsSpan[];\n      session_id?: string;\n    };\n  };\n}\n\nexport type FormatDocument<\n  // oxlint-disable-next-line typescript/no-explicit-any\n  Metadata extends Record<string, any> = Record<string, any>,\n> = (document: Document<Metadata>) => {\n  text: string;\n  id: string;\n  name: string;\n  score: number;\n};\n\nexport interface DatadogLLMObsTracerFields extends BaseCallbackHandlerInput {\n  mlApp: string;\n  userId?: string;\n  userHandle?: string;\n  sessionId?: string;\n  env?: string;\n  service?: string;\n  tags?: Record<string, string | undefined>;\n  ddApiKey?: string;\n  ddLLMObsEndpoint?: string;\n  formatDocument?: FormatDocument;\n}\n\nexport class DatadogLLMObsTracer\n  extends BaseTracer\n  implements DatadogLLMObsTracerFields\n{\n  name = \"datadog_tracer\";\n\n  ddLLMObsEndpoint?: string;\n\n  protected endpoint =\n    getEnvironmentVariable(\"DD_LLMOBS_ENDPOINT\") ||\n    \"https://api.datadoghq.com/api/unstable/llm-obs/v1/trace/spans\";\n\n  protected headers: Record<string, string> = {\n    \"Content-Type\": \"application/json\",\n  };\n\n  mlApp: string;\n\n  sessionId?: string;\n\n  tags: Record<string, string | undefined> = {};\n\n  formatDocument?: FormatDocument;\n\n  constructor(fields: DatadogLLMObsTracerFields) {\n    super(fields);\n    const {\n      mlApp,\n      userHandle,\n      userId,\n      sessionId,\n      service,\n      env,\n      tags,\n      ddLLMObsEndpoint,\n      ddApiKey,\n      formatDocument,\n    } = fields;\n\n    const apiKey = ddApiKey || getEnvironmentVariable(\"DD_API_KEY\");\n\n    if (apiKey) {\n      this.headers[\"DD-API-KEY\"] = apiKey;\n    }\n\n    this.mlApp = mlApp;\n    this.sessionId = sessionId;\n    this.ddLLMObsEndpoint = ddLLMObsEndpoint;\n    this.formatDocument = formatDocument;\n\n    this.tags = {\n      ...tags,\n      env: env || \"not-set\",\n      service: service || \"not-set\",\n      user_handle: userHandle,\n      user_id: userId,\n    };\n  }\n\n  protected async persistRun(_run: Run): Promise<void> {\n    try {\n      const spans = this.convertRunToDDSpans(_run);\n\n      const response = await fetch(this.ddLLMObsEndpoint || this.endpoint, {\n        method: \"POST\",\n        headers: this.headers,\n        body: JSON.stringify(this.formatRequestBody(spans)),\n      });\n\n      if (!response.ok) {\n        const error = await response.text();\n        throw new Error(error);\n      }\n    } catch (error) {\n      console.error(`Error writing spans to Datadog: ${error}`);\n    }\n  }\n\n  protected convertRunToDDSpans(run: Run): DatadogLLMObsSpan[] {\n    const spans = [this.langchainRunToDatadogLLMObsSpan(run)];\n\n    if (run.child_runs) {\n      run.child_runs.forEach((childRun) => {\n        spans.push(...this.convertRunToDDSpans(childRun));\n      });\n    }\n\n    return spans.flatMap((span) => (span ? [span] : []));\n  }\n\n  protected formatRequestBody(\n    spans: DatadogLLMObsSpan[]\n  ): DatadogLLMObsRequestBody {\n    return {\n      data: {\n        type: \"span\",\n        attributes: {\n          ml_app: this.mlApp,\n          tags: Object.entries(this.tags)\n            .filter(([, value]) => value)\n            .map(([key, value]) => `${key}:${value}`),\n          spans,\n          session_id: this.sessionId,\n        },\n      },\n    };\n  }\n\n  protected uuidToBigInt(uuid: string): string {\n    const hexString = uuid.replace(/-/g, \"\");\n    const first64Bits = hexString.slice(0, 16);\n    const bigIntValue = BigInt(`0x${first64Bits}`).toString();\n\n    return bigIntValue;\n  }\n\n  protected milisecondsToNanoseconds(ms: number): number {\n    return ms * 1e6;\n  }\n\n  protected toDatadogSpanKind(kind: string): DatadogLLMObsSpanKind | null {\n    switch (kind) {\n      case \"llm\":\n        return \"llm\";\n      case \"tool\":\n        return \"tool\";\n      case \"chain\":\n        return \"workflow\";\n      case \"retriever\":\n        return \"retrieval\";\n      default:\n        return null;\n    }\n  }\n\n  protected transformInput(\n    inputs: KVMap,\n    spanKind: DatadogLLMObsSpanKind\n  ): DatadogLLMObsIO {\n    if (spanKind === \"llm\") {\n      if (inputs?.messages) {\n        return {\n          messages: inputs?.messages?.flatMap((messages: BaseMessage[]) =>\n            messages.map((message) => ({\n              content: message.content,\n              role: message?._getType?.() ?? undefined,\n            }))\n          ),\n        };\n      }\n\n      if (inputs?.prompts) {\n        return { value: inputs.prompts.join(\"\\n\") };\n      }\n    }\n\n    return { value: JSON.stringify(inputs) };\n  }\n\n  protected transformOutput(\n    outputs: KVMap | undefined,\n    spanKind: DatadogLLMObsSpanKind\n  ): {\n    output: DatadogLLMObsIO | undefined;\n    tokensMetadata: Record<string, number>;\n  } {\n    const tokensMetadata: Record<string, number> = {};\n\n    if (!outputs) {\n      return { output: undefined, tokensMetadata };\n    }\n\n    if (spanKind === \"llm\") {\n      return {\n        output: {\n          messages: outputs?.generations?.flatMap(\n            (generations: ChatGeneration[]) =>\n              generations.map(({ message, text }) => {\n                if (isAIMessage(message) && message?.usage_metadata) {\n                  tokensMetadata.prompt_tokens =\n                    message.usage_metadata.input_tokens;\n                  tokensMetadata.completion_tokens =\n                    message.usage_metadata.output_tokens;\n                  tokensMetadata.total_tokens =\n                    message.usage_metadata.total_tokens;\n                }\n\n                return {\n                  content: message?.content ?? text,\n                  role: message?._getType?.(),\n                };\n              })\n          ),\n        },\n        tokensMetadata,\n      };\n    }\n\n    if (spanKind === \"retrieval\") {\n      return {\n        output: {\n          documents: outputs?.documents.map((document: Document) => {\n            if (typeof this.formatDocument === \"function\") {\n              return this.formatDocument(document);\n            }\n\n            return {\n              text: document.pageContent,\n              id: document.metadata?.id,\n              name: document.metadata?.name,\n              score: document.metadata?.score,\n            };\n          }),\n        },\n        tokensMetadata,\n      };\n    }\n\n    if (outputs?.output) {\n      return {\n        output: { value: JSON.stringify(outputs.output) },\n        tokensMetadata,\n      };\n    }\n\n    return { output: { value: JSON.stringify(outputs) }, tokensMetadata };\n  }\n\n  protected langchainRunToDatadogLLMObsSpan(\n    run: Run\n  ): DatadogLLMObsSpan | null {\n    if (!run.end_time || !run.trace_id) {\n      return null;\n    }\n\n    const spanId = this.uuidToBigInt(run.id);\n    const traceId = this.uuidToBigInt(run.trace_id);\n    const parentId = run.parent_run_id\n      ? this.uuidToBigInt(run.parent_run_id)\n      : \"undefined\";\n    const spanKind = this.toDatadogSpanKind(run.run_type);\n\n    if (spanKind === null) {\n      return null;\n    }\n\n    const input = this.transformInput(run.inputs, spanKind);\n    const { output, tokensMetadata } = this.transformOutput(\n      run.outputs,\n      spanKind\n    );\n\n    const startTimeNs = Number(this.milisecondsToNanoseconds(run.start_time));\n    const endTimeNs = Number(this.milisecondsToNanoseconds(run.end_time));\n    const durationNs = endTimeNs - startTimeNs;\n\n    if (durationNs <= 0) {\n      return null;\n    }\n\n    const spanName =\n      (run.serialized as { kwargs: { name?: string } })?.kwargs?.name ??\n      run.name;\n    const spanError = run.error ? 1 : 0;\n    const spanStatus = run.error ? \"error\" : \"ok\";\n\n    const meta = {\n      kind: spanKind,\n      input,\n      output,\n      model_name: run.extra?.metadata?.ls_model_name,\n      model_provider: run.extra?.metadata?.ls_provider,\n      temperature: run.extra?.metadata?.ls_temperature,\n    };\n\n    return {\n      parent_id: parentId,\n      trace_id: traceId,\n      span_id: spanId,\n      name: spanName,\n      error: spanError,\n      status: spanStatus,\n      tags: [...(run.tags?.length ? run.tags : [])],\n      meta,\n      start_ns: startTimeNs,\n      duration: durationNs,\n      metrics: tokensMetadata,\n    };\n  }\n}\n"],"mappings":"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"}