|
1 | 1 | 'use strict'
|
2 | 2 |
|
3 |
| -const { MEASURED } = require('../../../ext/tags') |
4 |
| -const { storage } = require('../../datadog-core') |
5 |
| -const TracingPlugin = require('../../dd-trace/src/plugins/tracing') |
6 |
| -const makeUtilities = require('../../dd-trace/src/plugins/util/llm') |
| 3 | +const CompositePlugin = require('../../dd-trace/src/plugins/composite') |
| 4 | +const GoogleVertexAITracingPlugin = require('./tracing') |
| 5 | +const VertexAILLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/vertexai') |
7 | 6 |
|
8 |
| -class GoogleCloudVertexAIPlugin extends TracingPlugin { |
| 7 | +class GoogleCloudVertexAIPlugin extends CompositePlugin { |
9 | 8 | static get id () { return 'google-cloud-vertexai' }
|
10 |
| - static get prefix () { |
11 |
| - return 'tracing:apm:vertexai:request' |
12 |
| - } |
13 |
| - |
14 |
| - constructor () { |
15 |
| - super(...arguments) |
16 |
| - |
17 |
| - Object.assign(this, makeUtilities('vertexai', this._tracerConfig)) |
18 |
| - } |
19 |
| - |
20 |
| - bindStart (ctx) { |
21 |
| - const { instance, request, resource, stream } = ctx |
22 |
| - |
23 |
| - const tags = this.tagRequest(request, instance, stream) |
24 |
| - |
25 |
| - const span = this.startSpan('vertexai.request', { |
26 |
| - service: this.config.service, |
27 |
| - resource, |
28 |
| - kind: 'client', |
29 |
| - meta: { |
30 |
| - [MEASURED]: 1, |
31 |
| - ...tags |
32 |
| - } |
33 |
| - }, false) |
34 |
| - |
35 |
| - const store = storage('legacy').getStore() || {} |
36 |
| - ctx.currentStore = { ...store, span } |
37 |
| - |
38 |
| - return ctx.currentStore |
39 |
| - } |
40 |
| - |
41 |
| - asyncEnd (ctx) { |
42 |
| - const span = ctx.currentStore?.span |
43 |
| - if (!span) return |
44 |
| - |
45 |
| - const { result } = ctx |
46 |
| - |
47 |
| - const response = result?.response |
48 |
| - if (response) { |
49 |
| - const tags = this.tagResponse(response) |
50 |
| - span.addTags(tags) |
51 |
| - } |
52 |
| - |
53 |
| - span.finish() |
54 |
| - } |
55 |
| - |
56 |
| - tagRequest (request, instance, stream) { |
57 |
| - const model = extractModel(instance) |
58 |
| - const tags = { |
59 |
| - 'vertexai.request.model': model |
60 |
| - } |
61 |
| - |
62 |
| - const history = instance.historyInternal |
63 |
| - let contents = typeof request === 'string' || Array.isArray(request) ? request : request.contents |
64 |
| - if (history) { |
65 |
| - contents = [...history, ...(Array.isArray(contents) ? contents : [contents])] |
66 |
| - } |
67 |
| - |
68 |
| - const generationConfig = instance.generationConfig || {} |
69 |
| - for (const key of Object.keys(generationConfig)) { |
70 |
| - const transformedKey = key.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase() |
71 |
| - tags[`vertexai.request.generation_config.${transformedKey}`] = JSON.stringify(generationConfig[key]) |
72 |
| - } |
73 |
| - |
74 |
| - if (stream) { |
75 |
| - tags['vertexai.request.stream'] = true |
76 |
| - } |
77 |
| - |
78 |
| - if (!this.isPromptCompletionSampled()) return tags |
79 |
| - |
80 |
| - const systemInstructions = extractSystemInstructions(instance) |
81 |
| - |
82 |
| - for (const [idx, systemInstruction] of systemInstructions.entries()) { |
83 |
| - tags[`vertexai.request.system_instruction.${idx}.text`] = systemInstruction |
84 |
| - } |
85 |
| - |
86 |
| - if (typeof contents === 'string') { |
87 |
| - tags['vertexai.request.contents.0.text'] = contents |
88 |
| - return tags |
89 |
| - } |
90 |
| - |
91 |
| - for (const [contentIdx, content] of contents.entries()) { |
92 |
| - this.tagRequestContent(tags, content, contentIdx) |
| 9 | + static get plugins () { |
| 10 | + return { |
| 11 | + llmobs: VertexAILLMObsPlugin, |
| 12 | + tracing: GoogleVertexAITracingPlugin |
93 | 13 | }
|
94 |
| - |
95 |
| - return tags |
96 | 14 | }
|
97 |
| - |
98 |
| - tagRequestPart (part, tags, partIdx, contentIdx) { |
99 |
| - tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.text`] = this.normalize(part.text) |
100 |
| - |
101 |
| - const functionCall = part.functionCall |
102 |
| - const functionResponse = part.functionResponse |
103 |
| - |
104 |
| - if (functionCall) { |
105 |
| - tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.name`] = functionCall.name |
106 |
| - tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.args`] = |
107 |
| - this.normalize(JSON.stringify(functionCall.args)) |
108 |
| - } |
109 |
| - if (functionResponse) { |
110 |
| - tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.name`] = |
111 |
| - functionResponse.name |
112 |
| - tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.response`] = |
113 |
| - this.normalize(JSON.stringify(functionResponse.response)) |
114 |
| - } |
115 |
| - } |
116 |
| - |
117 |
| - tagRequestContent (tags, content, contentIdx) { |
118 |
| - if (typeof content === 'string') { |
119 |
| - tags[`vertexai.request.contents.${contentIdx}.text`] = this.normalize(content) |
120 |
| - return |
121 |
| - } |
122 |
| - |
123 |
| - if (content.text || content.functionCall || content.functionResponse) { |
124 |
| - this.tagRequestPart(content, tags, 0, contentIdx) |
125 |
| - return |
126 |
| - } |
127 |
| - |
128 |
| - const { role, parts } = content |
129 |
| - if (role) { |
130 |
| - tags[`vertexai.request.contents.${contentIdx}.role`] = role |
131 |
| - } |
132 |
| - |
133 |
| - for (const [partIdx, part] of parts.entries()) { |
134 |
| - this.tagRequestPart(part, tags, partIdx, contentIdx) |
135 |
| - } |
136 |
| - } |
137 |
| - |
138 |
| - tagResponse (response) { |
139 |
| - const tags = {} |
140 |
| - |
141 |
| - const candidates = response.candidates |
142 |
| - for (const [candidateIdx, candidate] of candidates.entries()) { |
143 |
| - const finishReason = candidate.finishReason |
144 |
| - if (finishReason) { |
145 |
| - tags[`vertexai.response.candidates.${candidateIdx}.finish_reason`] = finishReason |
146 |
| - } |
147 |
| - const candidateContent = candidate.content |
148 |
| - const role = candidateContent.role |
149 |
| - tags[`vertexai.response.candidates.${candidateIdx}.content.role`] = role |
150 |
| - |
151 |
| - if (!this.isPromptCompletionSampled()) continue |
152 |
| - |
153 |
| - const parts = candidateContent.parts |
154 |
| - for (const [partIdx, part] of parts.entries()) { |
155 |
| - const text = part.text |
156 |
| - tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.text`] = |
157 |
| - this.normalize(String(text)) |
158 |
| - |
159 |
| - const functionCall = part.functionCall |
160 |
| - if (!functionCall) continue |
161 |
| - |
162 |
| - tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.name`] = |
163 |
| - functionCall.name |
164 |
| - tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.args`] = |
165 |
| - this.normalize(JSON.stringify(functionCall.args)) |
166 |
| - } |
167 |
| - } |
168 |
| - |
169 |
| - const tokenCounts = response.usageMetadata |
170 |
| - if (tokenCounts) { |
171 |
| - tags['vertexai.response.usage.prompt_tokens'] = tokenCounts.promptTokenCount |
172 |
| - tags['vertexai.response.usage.completion_tokens'] = tokenCounts.candidatesTokenCount |
173 |
| - tags['vertexai.response.usage.total_tokens'] = tokenCounts.totalTokenCount |
174 |
| - } |
175 |
| - |
176 |
| - return tags |
177 |
| - } |
178 |
| -} |
179 |
| - |
180 |
| -function extractModel (instance) { |
181 |
| - const model = instance.model || instance.resourcePath || instance.publisherModelEndpoint |
182 |
| - return model?.split('/').pop() |
183 |
| -} |
184 |
| - |
185 |
| -function extractSystemInstructions (instance) { |
186 |
| - // systemInstruction is either a string or a Content object |
187 |
| - // Content objects have parts (Part[]) and a role |
188 |
| - const systemInstruction = instance.systemInstruction |
189 |
| - if (!systemInstruction) return [] |
190 |
| - if (typeof systemInstruction === 'string') return [systemInstruction] |
191 |
| - |
192 |
| - return systemInstruction.parts?.map(part => part.text) |
193 | 15 | }
|
194 | 16 |
|
195 | 17 | module.exports = GoogleCloudVertexAIPlugin
|
0 commit comments