Skip to content

Commit

Permalink
Merge branch 'master' into juan-fernandez/fix-import-path-worker
Browse files Browse the repository at this point in the history
  • Loading branch information
juan-fernandez authored Jan 16, 2025
2 parents f59b87e + 6523d94 commit 0c29c21
Show file tree
Hide file tree
Showing 28 changed files with 1,955 additions and 106 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/llmobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,22 @@ jobs:
- uses: codecov/codecov-action@v3
- if: always()
uses: ./.github/actions/testagent/logs

langchain:
runs-on: ubuntu-latest
env:
PLUGINS: langchain
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/testagent/start
- uses: ./.github/actions/node/setup
- uses: ./.github/actions/install
- uses: ./.github/actions/node/18
- run: yarn test:llmobs:plugins:ci
shell: bash
- uses: ./.github/actions/node/latest
- run: yarn test:llmobs:plugins:ci
shell: bash
- uses: codecov/codecov-action@v3
- if: always()
uses: ./.github/actions/testagent/logs
10 changes: 10 additions & 0 deletions benchmark/sirun/profiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ if (PROFILER === 'space' || PROFILER === 'all') {
profilers.push(new SpaceProfiler())
}

if (profilers.length === 0) {
// Add a no-op "profiler"
profilers.push({
start: () => {},
stop: () => {},
profile: () => { return true },
encode: () => { Promise.resolve(true) }
})
}

const exporters = [{
export () {
profiler.stop()
Expand Down
3 changes: 0 additions & 3 deletions benchmark/sirun/runall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ else
source /usr/local/nvm/nvm.sh
fi

nvm use 18

# using Node.js v18 for the global yarn package
(
cd ../../ &&
npm install --global yarn \
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"test:lambda:ci": "nyc --no-clean --include \"packages/dd-trace/src/lambda/**/*.js\" -- npm run test:lambda",
"test:llmobs:sdk": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" --exclude \"packages/dd-trace/test/llmobs/plugins/**/*.spec.js\" \"packages/dd-trace/test/llmobs/**/*.spec.js\" ",
"test:llmobs:sdk:ci": "nyc --no-clean --include \"packages/dd-trace/src/llmobs/**/*.js\" -- npm run test:llmobs:sdk",
"test:llmobs:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/llmobs/plugins/**/*.spec.js\"",
"test:llmobs:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/llmobs/plugins/@($(echo $PLUGINS))/*.spec.js\"",
"test:llmobs:plugins:ci": "yarn services && nyc --no-clean --include \"packages/dd-trace/src/llmobs/**/*.js\" -- npm run test:llmobs:plugins",
"test:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\"",
"test:plugins:ci": "yarn services && nyc --no-clean --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS)).js\" --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS))/**/*.js\" --include \"packages/datadog-plugin-@($(echo $PLUGINS))/src/**/*.js\" -- npm run test:plugins",
Expand Down
2 changes: 2 additions & 0 deletions packages/datadog-instrumentations/src/openai.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ for (const shim of V4_PACKAGE_SHIMS) {
})
})

ch.end.publish(ctx)

return apiProm
})
})
Expand Down
92 changes: 12 additions & 80 deletions packages/datadog-plugin-langchain/src/index.js
Original file line number Diff line number Diff line change
@@ -1,89 +1,21 @@
'use strict'

const { MEASURED } = require('../../../ext/tags')
const { storage } = require('../../datadog-core')
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
const LangChainTracingPlugin = require('./tracing')
const LangChainLLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/langchain')
const CompositePlugin = require('../../dd-trace/src/plugins/composite')

const API_KEY = 'langchain.request.api_key'
const MODEL = 'langchain.request.model'
const PROVIDER = 'langchain.request.provider'
const TYPE = 'langchain.request.type'

const LangChainHandler = require('./handlers/default')
const LangChainChatModelHandler = require('./handlers/language_models/chat_model')
const LangChainLLMHandler = require('./handlers/language_models/llm')
const LangChainChainHandler = require('./handlers/chain')
const LangChainEmbeddingHandler = require('./handlers/embedding')

class LangChainPlugin extends TracingPlugin {
class LangChainPlugin extends CompositePlugin {
static get id () { return 'langchain' }
static get operation () { return 'invoke' }
static get system () { return 'langchain' }
static get prefix () {
return 'tracing:apm:langchain:invoke'
}

constructor () {
super(...arguments)

const langchainConfig = this._tracerConfig.langchain || {}
this.handlers = {
chain: new LangChainChainHandler(langchainConfig),
chat_model: new LangChainChatModelHandler(langchainConfig),
llm: new LangChainLLMHandler(langchainConfig),
embedding: new LangChainEmbeddingHandler(langchainConfig),
default: new LangChainHandler(langchainConfig)
static get plugins () {
return {
// ordering here is important - the llm observability plugin must come first
// so that we can add annotations associated with the span before it finishes.
// however, because the tracing plugin uses `bindStart` vs the llmobs' `start`,
// the span is guaranteed to be created in the tracing plugin before the llmobs one is called
llmobs: LangChainLLMObsPlugin,
tracing: LangChainTracingPlugin
}
}

bindStart (ctx) {
const { resource, type } = ctx
const handler = this.handlers[type]

const instance = ctx.instance
const apiKey = handler.extractApiKey(instance)
const provider = handler.extractProvider(instance)
const model = handler.extractModel(instance)

const tags = handler.getSpanStartTags(ctx, provider) || []

if (apiKey) tags[API_KEY] = apiKey
if (provider) tags[PROVIDER] = provider
if (model) tags[MODEL] = model
if (type) tags[TYPE] = type

const span = this.startSpan('langchain.request', {
service: this.config.service,
resource,
kind: 'client',
meta: {
[MEASURED]: 1,
...tags
}
}, false)

const store = storage.getStore() || {}
ctx.currentStore = { ...store, span }

return ctx.currentStore
}

asyncEnd (ctx) {
const span = ctx.currentStore.span

const { type } = ctx

const handler = this.handlers[type]
const tags = handler.getSpanEndTags(ctx) || {}

span.addTags(tags)

span.finish()
}

getHandler (type) {
return this.handlers[type] || this.handlers.default
}
}

module.exports = LangChainPlugin
89 changes: 89 additions & 0 deletions packages/datadog-plugin-langchain/src/tracing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict'

const { MEASURED } = require('../../../ext/tags')
const { storage } = require('../../datadog-core')
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')

const API_KEY = 'langchain.request.api_key'
const MODEL = 'langchain.request.model'
const PROVIDER = 'langchain.request.provider'
const TYPE = 'langchain.request.type'

const LangChainHandler = require('./handlers/default')
const LangChainChatModelHandler = require('./handlers/language_models/chat_model')
const LangChainLLMHandler = require('./handlers/language_models/llm')
const LangChainChainHandler = require('./handlers/chain')
const LangChainEmbeddingHandler = require('./handlers/embedding')

class LangChainTracingPlugin extends TracingPlugin {
static get id () { return 'langchain' }
static get operation () { return 'invoke' }
static get system () { return 'langchain' }
static get prefix () {
return 'tracing:apm:langchain:invoke'
}

constructor () {
super(...arguments)

const langchainConfig = this._tracerConfig.langchain || {}
this.handlers = {
chain: new LangChainChainHandler(langchainConfig),
chat_model: new LangChainChatModelHandler(langchainConfig),
llm: new LangChainLLMHandler(langchainConfig),
embedding: new LangChainEmbeddingHandler(langchainConfig),
default: new LangChainHandler(langchainConfig)
}
}

bindStart (ctx) {
const { resource, type } = ctx
const handler = this.handlers[type]

const instance = ctx.instance
const apiKey = handler.extractApiKey(instance)
const provider = handler.extractProvider(instance)
const model = handler.extractModel(instance)

const tags = handler.getSpanStartTags(ctx, provider) || []

if (apiKey) tags[API_KEY] = apiKey
if (provider) tags[PROVIDER] = provider
if (model) tags[MODEL] = model
if (type) tags[TYPE] = type

const span = this.startSpan('langchain.request', {
service: this.config.service,
resource,
kind: 'client',
meta: {
[MEASURED]: 1,
...tags
}
}, false)

const store = storage.getStore() || {}
ctx.currentStore = { ...store, span }

return ctx.currentStore
}

asyncEnd (ctx) {
const span = ctx.currentStore.span

const { type } = ctx

const handler = this.handlers[type]
const tags = handler.getSpanEndTags(ctx) || {}

span.addTags(tags)

span.finish()
}

getHandler (type) {
return this.handlers[type] || this.handlers.default
}
}

module.exports = LangChainTracingPlugin
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const REDACTED_IDENTIFIERS = new Set(
'email',
'encryption_key',
'encryptionkeyid',
'env',
'geo_location',
'gpg_key',
'ip_address',
Expand Down Expand Up @@ -75,6 +76,7 @@ const REDACTED_IDENTIFIERS = new Set(
'salt',
'secret',
'secretKey',
'secrettoken',
'securitycode',
'security_answer',
'security_question',
Expand Down
51 changes: 40 additions & 11 deletions packages/dd-trace/src/llmobs/plugins/base.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,60 @@
'use strict'

const log = require('../../log')
const { storage } = require('../storage')
const { storage: llmobsStorage } = require('../storage')

const TracingPlugin = require('../../plugins/tracing')
const LLMObsTagger = require('../tagger')

// we make this a `Plugin` so we don't have to worry about `finish` being called
class LLMObsPlugin extends TracingPlugin {
constructor (...args) {
super(...args)

this._tagger = new LLMObsTagger(this._tracerConfig, true)
}

getName () {}

setLLMObsTags (ctx) {
throw new Error('setLLMObsTags must be implemented by the subclass')
}

getLLMObsSPanRegisterOptions (ctx) {
getLLMObsSpanRegisterOptions (ctx) {
throw new Error('getLLMObsSPanRegisterOptions must be implemented by the subclass')
}

start (ctx) {
const oldStore = storage.getStore()
const parent = oldStore?.span
const span = ctx.currentStore?.span
// even though llmobs span events won't be enqueued if llmobs is disabled
// we should avoid doing any computations here (these listeners aren't disabled)
const enabled = this._tracerConfig.llmobs.enabled
if (!enabled) return

const parent = this.getLLMObsParent(ctx)
const apmStore = ctx.currentStore
const span = apmStore?.span

const registerOptions = this.getLLMObsSpanRegisterOptions(ctx)

// register options may not be set for operations we do not trace with llmobs
// ie OpenAI fine tuning jobs, file jobs, etc.
if (registerOptions) {
ctx.llmobs = {} // initialize context-based namespace
llmobsStorage.enterWith({ span })
ctx.llmobs.parent = parent

const registerOptions = this.getLLMObsSPanRegisterOptions(ctx)
this._tagger.registerLLMObsSpan(span, { parent, ...registerOptions })
}
}

end (ctx) {
const enabled = this._tracerConfig.llmobs.enabled
if (!enabled) return

// only attempt to restore the context if the current span was an LLMObs span
const apmStore = ctx.currentStore
const span = apmStore?.span
if (!LLMObsTagger.tagMap.has(span)) return

this._tagger.registerLLMObsSpan(span, { parent, ...registerOptions })
const parent = ctx.llmobs.parent
llmobsStorage.enterWith({ span: parent })
}

asyncEnd (ctx) {
Expand All @@ -40,7 +63,8 @@ class LLMObsPlugin extends TracingPlugin {
const enabled = this._tracerConfig.llmobs.enabled
if (!enabled) return

const span = ctx.currentStore?.span
const apmStore = ctx.currentStore
const span = apmStore?.span
if (!span) {
log.debug(
`Tried to start an LLMObs span for ${this.constructor.name} without an active APM span.
Expand All @@ -60,6 +84,11 @@ class LLMObsPlugin extends TracingPlugin {
}
super.configure(config)
}

getLLMObsParent () {
const store = llmobsStorage.getStore()
return store?.span
}
}

module.exports = LLMObsPlugin
24 changes: 24 additions & 0 deletions packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict'

const LangChainLLMObsHandler = require('.')
const { spanHasError } = require('../../../util')

class LangChainLLMObsChainHandler extends LangChainLLMObsHandler {
setMetaTags ({ span, inputs, results }) {
let input, output
if (inputs) {
input = this.formatIO(inputs)
}

if (!results || spanHasError(span)) {
output = ''
} else {
output = this.formatIO(results)
}

// chain spans will always be workflows
this._tagger.tagTextIO(span, input, output)
}
}

module.exports = LangChainLLMObsChainHandler
Loading

0 comments on commit 0c29c21

Please sign in to comment.