@langchain/community
Version:
Third-party integrations for LangChain.js
1 lines • 13.5 kB
Source Map (JSON)
{"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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"}