@arizeai/openinference-instrumentation-openai 1.1.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/instrumentation.js +229 -402
- package/dist/esm/instrumentation.js.map +1 -1
- package/dist/esm/package.json +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/esnext/instrumentation.js +3 -0
- package/dist/esnext/instrumentation.js.map +1 -1
- package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +1 -1
- package/dist/src/instrumentation.js +3 -0
- package/dist/src/instrumentation.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -6
- package/src/instrumentation.ts +3 -0
- package/src/version.ts +1 -1
- package/dist/test/openai.test.d.ts +0 -2
- package/dist/test/openai.test.d.ts.map +0 -1
- package/dist/test/openai.test.js +0 -1057
- package/dist/test/openai.test.js.map +0 -1
|
@@ -1,123 +1,16 @@
|
|
|
1
|
-
var __extends = (this && this.__extends) || (function () {
|
|
2
|
-
var extendStatics = function (d, b) {
|
|
3
|
-
extendStatics = Object.setPrototypeOf ||
|
|
4
|
-
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
5
|
-
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
6
|
-
return extendStatics(d, b);
|
|
7
|
-
};
|
|
8
|
-
return function (d, b) {
|
|
9
|
-
if (typeof b !== "function" && b !== null)
|
|
10
|
-
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
11
|
-
extendStatics(d, b);
|
|
12
|
-
function __() { this.constructor = d; }
|
|
13
|
-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
14
|
-
};
|
|
15
|
-
})();
|
|
16
|
-
var __assign = (this && this.__assign) || function () {
|
|
17
|
-
__assign = Object.assign || function(t) {
|
|
18
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
19
|
-
s = arguments[i];
|
|
20
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
21
|
-
t[p] = s[p];
|
|
22
|
-
}
|
|
23
|
-
return t;
|
|
24
|
-
};
|
|
25
|
-
return __assign.apply(this, arguments);
|
|
26
|
-
};
|
|
27
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
28
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
29
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
30
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
31
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
32
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
33
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
34
|
-
});
|
|
35
|
-
};
|
|
36
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
37
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
38
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
39
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
40
|
-
function step(op) {
|
|
41
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
42
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
43
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
44
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
45
|
-
switch (op[0]) {
|
|
46
|
-
case 0: case 1: t = op; break;
|
|
47
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
48
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
49
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
50
|
-
default:
|
|
51
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
52
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
53
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
54
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
55
|
-
if (t[2]) _.ops.pop();
|
|
56
|
-
_.trys.pop(); continue;
|
|
57
|
-
}
|
|
58
|
-
op = body.call(thisArg, _);
|
|
59
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
60
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
64
|
-
var t = {};
|
|
65
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
66
|
-
t[p] = s[p];
|
|
67
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
68
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
69
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
70
|
-
t[p[i]] = s[p[i]];
|
|
71
|
-
}
|
|
72
|
-
return t;
|
|
73
|
-
};
|
|
74
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
75
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
76
|
-
var m = o[Symbol.asyncIterator], i;
|
|
77
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
78
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
79
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
80
|
-
};
|
|
81
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
82
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
83
|
-
if (!m) return o;
|
|
84
|
-
var i = m.call(o), r, ar = [], e;
|
|
85
|
-
try {
|
|
86
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
87
|
-
}
|
|
88
|
-
catch (error) { e = { error: error }; }
|
|
89
|
-
finally {
|
|
90
|
-
try {
|
|
91
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
92
|
-
}
|
|
93
|
-
finally { if (e) throw e.error; }
|
|
94
|
-
}
|
|
95
|
-
return ar;
|
|
96
|
-
};
|
|
97
|
-
var __values = (this && this.__values) || function(o) {
|
|
98
|
-
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
99
|
-
if (m) return m.call(o);
|
|
100
|
-
if (o && typeof o.length === "number") return {
|
|
101
|
-
next: function () {
|
|
102
|
-
if (o && i >= o.length) o = void 0;
|
|
103
|
-
return { value: o && o[i++], done: !o };
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
107
|
-
};
|
|
108
1
|
import { InstrumentationBase, InstrumentationNodeModuleDefinition, safeExecuteInTheMiddle, } from "@opentelemetry/instrumentation";
|
|
109
2
|
import { diag, context, trace, SpanKind, SpanStatusCode, } from "@opentelemetry/api";
|
|
110
|
-
import { VERSION } from "./version";
|
|
3
|
+
import { VERSION } from "./version.js";
|
|
111
4
|
import { SemanticConventions, OpenInferenceSpanKind, MimeType, LLMSystem, LLMProvider, } from "@arizeai/openinference-semantic-conventions";
|
|
112
|
-
import { assertUnreachable, isString } from "./typeUtils";
|
|
5
|
+
import { assertUnreachable, isString } from "./typeUtils.js";
|
|
113
6
|
import { isTracingSuppressed } from "@opentelemetry/core";
|
|
114
7
|
import { OITracer, safelyJSONStringify, } from "@arizeai/openinference-core";
|
|
115
|
-
|
|
8
|
+
const MODULE_NAME = "openai";
|
|
116
9
|
/**
|
|
117
10
|
* Flag to check if the openai module has been patched
|
|
118
11
|
* Note: This is a fallback in case the module is made immutable (e.x. Deno, webpack, etc.)
|
|
119
12
|
*/
|
|
120
|
-
|
|
13
|
+
let _isOpenInferencePatched = false;
|
|
121
14
|
/**
|
|
122
15
|
* function to check if instrumentation is enabled / disabled
|
|
123
16
|
*/
|
|
@@ -130,9 +23,9 @@ export function isPatched() {
|
|
|
130
23
|
* @param span
|
|
131
24
|
*/
|
|
132
25
|
function getExecContext(span) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
26
|
+
const activeContext = context.active();
|
|
27
|
+
const suppressTracing = isTracingSuppressed(activeContext);
|
|
28
|
+
const execContext = suppressTracing
|
|
136
29
|
? trace.setSpan(context.active(), span)
|
|
137
30
|
: activeContext;
|
|
138
31
|
// Drop the span from the context
|
|
@@ -146,59 +39,60 @@ function getExecContext(span) {
|
|
|
146
39
|
* @param instrumentationConfig The config for the instrumentation @see {@link InstrumentationConfig}
|
|
147
40
|
* @param traceConfig The OpenInference trace configuration. Can be used to mask or redact sensitive information on spans. @see {@link TraceConfigOptions}
|
|
148
41
|
*/
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
var _this = _super.call(this, "@arizeai/openinference-instrumentation-openai", VERSION, Object.assign({}, instrumentationConfig)) || this;
|
|
154
|
-
_this.oiTracer = new OITracer({ tracer: _this.tracer, traceConfig: traceConfig });
|
|
155
|
-
return _this;
|
|
42
|
+
export class OpenAIInstrumentation extends InstrumentationBase {
|
|
43
|
+
constructor({ instrumentationConfig, traceConfig, } = {}) {
|
|
44
|
+
super("@arizeai/openinference-instrumentation-openai", VERSION, Object.assign({}, instrumentationConfig));
|
|
45
|
+
this.oiTracer = new OITracer({ tracer: this.tracer, traceConfig });
|
|
156
46
|
}
|
|
157
|
-
|
|
158
|
-
|
|
47
|
+
init() {
|
|
48
|
+
const module = new InstrumentationNodeModuleDefinition("openai", ["^4.0.0"], this.patch.bind(this), this.unpatch.bind(this));
|
|
159
49
|
return module;
|
|
160
|
-
}
|
|
50
|
+
}
|
|
161
51
|
/**
|
|
162
52
|
* Manually instruments the OpenAI module. This is needed when the module is not loaded via require (commonjs)
|
|
163
53
|
* @param {openai} module
|
|
164
54
|
*/
|
|
165
|
-
|
|
166
|
-
diag.debug(
|
|
55
|
+
manuallyInstrument(module) {
|
|
56
|
+
diag.debug(`Manually instrumenting ${MODULE_NAME}`);
|
|
167
57
|
this.patch(module);
|
|
168
|
-
}
|
|
58
|
+
}
|
|
169
59
|
/**
|
|
170
60
|
* Patches the OpenAI module
|
|
171
61
|
*/
|
|
172
|
-
|
|
173
|
-
diag.debug(
|
|
174
|
-
if (
|
|
62
|
+
patch(module, moduleVersion) {
|
|
63
|
+
diag.debug(`Applying patch for ${MODULE_NAME}@${moduleVersion}`);
|
|
64
|
+
if (module?.openInferencePatched || _isOpenInferencePatched) {
|
|
175
65
|
return module;
|
|
176
66
|
}
|
|
177
67
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
178
|
-
|
|
68
|
+
const instrumentation = this;
|
|
179
69
|
// Patch create chat completions
|
|
180
70
|
this._wrap(module.OpenAI.Chat.Completions.prototype, "create",
|
|
181
71
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
182
|
-
|
|
183
|
-
return function patchedCreate() {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
188
|
-
args[_i] = arguments[_i];
|
|
189
|
-
}
|
|
190
|
-
var body = args[0];
|
|
191
|
-
var _messages = body.messages, invocationParameters = __rest(body, ["messages"]);
|
|
192
|
-
var span = instrumentation.oiTracer.startSpan("OpenAI Chat Completions", {
|
|
72
|
+
(original) => {
|
|
73
|
+
return function patchedCreate(...args) {
|
|
74
|
+
const body = args[0];
|
|
75
|
+
const { messages: _messages, ...invocationParameters } = body;
|
|
76
|
+
const span = instrumentation.oiTracer.startSpan(`OpenAI Chat Completions`, {
|
|
193
77
|
kind: SpanKind.INTERNAL,
|
|
194
|
-
attributes:
|
|
78
|
+
attributes: {
|
|
79
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
|
|
80
|
+
[SemanticConventions.LLM_MODEL_NAME]: body.model,
|
|
81
|
+
[SemanticConventions.INPUT_VALUE]: JSON.stringify(body),
|
|
82
|
+
[SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
|
|
83
|
+
[SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(invocationParameters),
|
|
84
|
+
[SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
|
|
85
|
+
[SemanticConventions.LLM_PROVIDER]: LLMProvider.OPENAI,
|
|
86
|
+
...getLLMInputMessagesAttributes(body),
|
|
87
|
+
...getLLMToolsJSONSchema(body),
|
|
88
|
+
},
|
|
195
89
|
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return context.with(trace.setSpan(execContext, span),
|
|
199
|
-
return original.apply(
|
|
90
|
+
const execContext = getExecContext(span);
|
|
91
|
+
const execPromise = safeExecuteInTheMiddle(() => {
|
|
92
|
+
return context.with(trace.setSpan(execContext, span), () => {
|
|
93
|
+
return original.apply(this, args);
|
|
200
94
|
});
|
|
201
|
-
},
|
|
95
|
+
}, (error) => {
|
|
202
96
|
// Push the error to the span
|
|
203
97
|
if (error) {
|
|
204
98
|
span.recordException(error);
|
|
@@ -209,11 +103,17 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
209
103
|
span.end();
|
|
210
104
|
}
|
|
211
105
|
});
|
|
212
|
-
|
|
213
|
-
var _a;
|
|
106
|
+
const wrappedPromise = execPromise.then((result) => {
|
|
214
107
|
if (isChatCompletionResponse(result)) {
|
|
215
108
|
// Record the results
|
|
216
|
-
span.setAttributes(
|
|
109
|
+
span.setAttributes({
|
|
110
|
+
[SemanticConventions.OUTPUT_VALUE]: JSON.stringify(result),
|
|
111
|
+
[SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
|
|
112
|
+
// Override the model from the value sent by the server
|
|
113
|
+
[SemanticConventions.LLM_MODEL_NAME]: result.model,
|
|
114
|
+
...getChatCompletionLLMOutputMessagesAttributes(result),
|
|
115
|
+
...getUsageAttributes(result),
|
|
116
|
+
});
|
|
217
117
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
218
118
|
span.end();
|
|
219
119
|
}
|
|
@@ -221,7 +121,7 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
221
121
|
// This is a streaming response
|
|
222
122
|
// handle the chunks and add them to the span
|
|
223
123
|
// First split the stream via tee
|
|
224
|
-
|
|
124
|
+
const [leftStream, rightStream] = result.tee();
|
|
225
125
|
consumeChatCompletionStreamChunks(rightStream, span);
|
|
226
126
|
result = leftStream;
|
|
227
127
|
}
|
|
@@ -232,26 +132,27 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
232
132
|
});
|
|
233
133
|
this._wrap(module.OpenAI.Completions.prototype, "create",
|
|
234
134
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
235
|
-
|
|
236
|
-
return function patchedCreate() {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
241
|
-
args[_i] = arguments[_i];
|
|
242
|
-
}
|
|
243
|
-
var body = args[0];
|
|
244
|
-
var _prompt = body.prompt, invocationParameters = __rest(body, ["prompt"]);
|
|
245
|
-
var span = instrumentation.oiTracer.startSpan("OpenAI Completions", {
|
|
135
|
+
(original) => {
|
|
136
|
+
return function patchedCreate(...args) {
|
|
137
|
+
const body = args[0];
|
|
138
|
+
const { prompt: _prompt, ...invocationParameters } = body;
|
|
139
|
+
const span = instrumentation.oiTracer.startSpan(`OpenAI Completions`, {
|
|
246
140
|
kind: SpanKind.INTERNAL,
|
|
247
|
-
attributes:
|
|
141
|
+
attributes: {
|
|
142
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
|
|
143
|
+
[SemanticConventions.LLM_MODEL_NAME]: body.model,
|
|
144
|
+
[SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(invocationParameters),
|
|
145
|
+
[SemanticConventions.LLM_SYSTEM]: LLMSystem.OPENAI,
|
|
146
|
+
[SemanticConventions.LLM_PROVIDER]: LLMProvider.OPENAI,
|
|
147
|
+
...getCompletionInputValueAndMimeType(body),
|
|
148
|
+
},
|
|
248
149
|
});
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return context.with(trace.setSpan(execContext, span),
|
|
252
|
-
return original.apply(
|
|
150
|
+
const execContext = getExecContext(span);
|
|
151
|
+
const execPromise = safeExecuteInTheMiddle(() => {
|
|
152
|
+
return context.with(trace.setSpan(execContext, span), () => {
|
|
153
|
+
return original.apply(this, args);
|
|
253
154
|
});
|
|
254
|
-
},
|
|
155
|
+
}, (error) => {
|
|
255
156
|
// Push the error to the span
|
|
256
157
|
if (error) {
|
|
257
158
|
span.recordException(error);
|
|
@@ -262,11 +163,17 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
262
163
|
span.end();
|
|
263
164
|
}
|
|
264
165
|
});
|
|
265
|
-
|
|
266
|
-
var _a;
|
|
166
|
+
const wrappedPromise = execPromise.then((result) => {
|
|
267
167
|
if (isCompletionResponse(result)) {
|
|
268
168
|
// Record the results
|
|
269
|
-
span.setAttributes(
|
|
169
|
+
span.setAttributes({
|
|
170
|
+
[SemanticConventions.OUTPUT_VALUE]: JSON.stringify(result),
|
|
171
|
+
[SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
|
|
172
|
+
// Override the model from the value sent by the server
|
|
173
|
+
[SemanticConventions.LLM_MODEL_NAME]: result.model,
|
|
174
|
+
...getCompletionOutputValueAndMimeType(result),
|
|
175
|
+
...getUsageAttributes(result),
|
|
176
|
+
});
|
|
270
177
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
271
178
|
span.end();
|
|
272
179
|
}
|
|
@@ -277,31 +184,31 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
277
184
|
});
|
|
278
185
|
this._wrap(module.OpenAI.Embeddings.prototype, "create",
|
|
279
186
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
280
|
-
|
|
281
|
-
return function patchedEmbeddingCreate() {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
args[_i] = arguments[_i];
|
|
287
|
-
}
|
|
288
|
-
var body = args[0];
|
|
289
|
-
var input = body.input;
|
|
290
|
-
var isStringInput = typeof input === "string";
|
|
291
|
-
var span = instrumentation.oiTracer.startSpan("OpenAI Embeddings", {
|
|
187
|
+
(original) => {
|
|
188
|
+
return function patchedEmbeddingCreate(...args) {
|
|
189
|
+
const body = args[0];
|
|
190
|
+
const { input } = body;
|
|
191
|
+
const isStringInput = typeof input === "string";
|
|
192
|
+
const span = instrumentation.oiTracer.startSpan(`OpenAI Embeddings`, {
|
|
292
193
|
kind: SpanKind.INTERNAL,
|
|
293
|
-
attributes:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
194
|
+
attributes: {
|
|
195
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.EMBEDDING,
|
|
196
|
+
[SemanticConventions.EMBEDDING_MODEL_NAME]: body.model,
|
|
197
|
+
[SemanticConventions.INPUT_VALUE]: isStringInput
|
|
198
|
+
? input
|
|
199
|
+
: JSON.stringify(input),
|
|
200
|
+
[SemanticConventions.INPUT_MIME_TYPE]: isStringInput
|
|
201
|
+
? MimeType.TEXT
|
|
202
|
+
: MimeType.JSON,
|
|
203
|
+
...getEmbeddingTextAttributes(body),
|
|
204
|
+
},
|
|
298
205
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return context.with(trace.setSpan(execContext, span),
|
|
302
|
-
return original.apply(
|
|
206
|
+
const execContext = getExecContext(span);
|
|
207
|
+
const execPromise = safeExecuteInTheMiddle(() => {
|
|
208
|
+
return context.with(trace.setSpan(execContext, span), () => {
|
|
209
|
+
return original.apply(this, args);
|
|
303
210
|
});
|
|
304
|
-
},
|
|
211
|
+
}, (error) => {
|
|
305
212
|
// Push the error to the span
|
|
306
213
|
if (error) {
|
|
307
214
|
span.recordException(error);
|
|
@@ -312,10 +219,13 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
312
219
|
span.end();
|
|
313
220
|
}
|
|
314
221
|
});
|
|
315
|
-
|
|
222
|
+
const wrappedPromise = execPromise.then((result) => {
|
|
316
223
|
if (result) {
|
|
317
224
|
// Record the results
|
|
318
|
-
span.setAttributes(
|
|
225
|
+
span.setAttributes({
|
|
226
|
+
// Do not record the output data as it can be large
|
|
227
|
+
...getEmbeddingEmbeddingsAttributes(result),
|
|
228
|
+
});
|
|
319
229
|
}
|
|
320
230
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
321
231
|
span.end();
|
|
@@ -330,15 +240,15 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
330
240
|
module.openInferencePatched = true;
|
|
331
241
|
}
|
|
332
242
|
catch (e) {
|
|
333
|
-
diag.debug(
|
|
243
|
+
diag.debug(`Failed to set ${MODULE_NAME} patched flag on the module`, e);
|
|
334
244
|
}
|
|
335
245
|
return module;
|
|
336
|
-
}
|
|
246
|
+
}
|
|
337
247
|
/**
|
|
338
248
|
* Un-patches the OpenAI module's chat completions API
|
|
339
249
|
*/
|
|
340
|
-
|
|
341
|
-
diag.debug(
|
|
250
|
+
unpatch(moduleExports, moduleVersion) {
|
|
251
|
+
diag.debug(`Removing patch for ${MODULE_NAME}@${moduleVersion}`);
|
|
342
252
|
this._unwrap(moduleExports.OpenAI.Chat.Completions.prototype, "create");
|
|
343
253
|
this._unwrap(moduleExports.OpenAI.Completions.prototype, "create");
|
|
344
254
|
this._unwrap(moduleExports.OpenAI.Embeddings.prototype, "create");
|
|
@@ -348,12 +258,10 @@ var OpenAIInstrumentation = /** @class */ (function (_super) {
|
|
|
348
258
|
moduleExports.openInferencePatched = false;
|
|
349
259
|
}
|
|
350
260
|
catch (e) {
|
|
351
|
-
diag.warn(
|
|
261
|
+
diag.warn(`Failed to unset ${MODULE_NAME} patched flag on the module`, e);
|
|
352
262
|
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
}(InstrumentationBase));
|
|
356
|
-
export { OpenAIInstrumentation };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
357
265
|
/**
|
|
358
266
|
* type-guard that checks if the response is a chat completion response
|
|
359
267
|
*/
|
|
@@ -370,29 +278,18 @@ function isCompletionResponse(response) {
|
|
|
370
278
|
* type-guard that checks if completion prompt attribute is an array of strings
|
|
371
279
|
*/
|
|
372
280
|
function isPromptStringArray(prompt) {
|
|
373
|
-
return (Array.isArray(prompt) && prompt.every(
|
|
281
|
+
return (Array.isArray(prompt) && prompt.every((item) => typeof item === "string"));
|
|
374
282
|
}
|
|
375
283
|
/**
|
|
376
284
|
* Converts the body of a chat completions request to LLM input messages
|
|
377
285
|
*/
|
|
378
286
|
function getLLMInputMessagesAttributes(body) {
|
|
379
|
-
return body.messages.reduce(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
for (var _b = __values(Object.entries(messageAttributes)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
386
|
-
var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
|
|
387
|
-
acc["".concat(indexPrefix).concat(key)] = value;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
391
|
-
finally {
|
|
392
|
-
try {
|
|
393
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
394
|
-
}
|
|
395
|
-
finally { if (e_1) throw e_1.error; }
|
|
287
|
+
return body.messages.reduce((acc, message, index) => {
|
|
288
|
+
const messageAttributes = getChatCompletionInputMessageAttributes(message);
|
|
289
|
+
const indexPrefix = `${SemanticConventions.LLM_INPUT_MESSAGES}.${index}.`;
|
|
290
|
+
// Flatten the attributes on the index prefix
|
|
291
|
+
for (const [key, value] of Object.entries(messageAttributes)) {
|
|
292
|
+
acc[`${indexPrefix}${key}`] = value;
|
|
396
293
|
}
|
|
397
294
|
return acc;
|
|
398
295
|
}, {});
|
|
@@ -405,9 +302,9 @@ function getLLMToolsJSONSchema(body) {
|
|
|
405
302
|
// If tools is undefined, return an empty object
|
|
406
303
|
return {};
|
|
407
304
|
}
|
|
408
|
-
return body.tools.reduce(
|
|
409
|
-
|
|
410
|
-
|
|
305
|
+
return body.tools.reduce((acc, tool, index) => {
|
|
306
|
+
const toolJsonSchema = safelyJSONStringify(tool);
|
|
307
|
+
const key = `${SemanticConventions.LLM_TOOLS}.${index}.${SemanticConventions.TOOL_JSON_SCHEMA}`;
|
|
411
308
|
if (toolJsonSchema) {
|
|
412
309
|
acc[key] = toolJsonSchema;
|
|
413
310
|
}
|
|
@@ -415,25 +312,24 @@ function getLLMToolsJSONSchema(body) {
|
|
|
415
312
|
}, {});
|
|
416
313
|
}
|
|
417
314
|
function getChatCompletionInputMessageAttributes(message) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
_a);
|
|
315
|
+
const role = message.role;
|
|
316
|
+
const attributes = {
|
|
317
|
+
[SemanticConventions.MESSAGE_ROLE]: role,
|
|
318
|
+
};
|
|
423
319
|
// Add the content only if it is a string
|
|
424
320
|
if (typeof message.content === "string") {
|
|
425
321
|
attributes[SemanticConventions.MESSAGE_CONTENT] = message.content;
|
|
426
322
|
}
|
|
427
323
|
else if (Array.isArray(message.content)) {
|
|
428
|
-
message.content.forEach(
|
|
429
|
-
|
|
324
|
+
message.content.forEach((part, index) => {
|
|
325
|
+
const contentsIndexPrefix = `${SemanticConventions.MESSAGE_CONTENTS}.${index}.`;
|
|
430
326
|
if (part.type === "text") {
|
|
431
|
-
attributes[
|
|
432
|
-
attributes[
|
|
327
|
+
attributes[`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`] = "text";
|
|
328
|
+
attributes[`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TEXT}`] = part.text;
|
|
433
329
|
}
|
|
434
330
|
else if (part.type === "image_url") {
|
|
435
|
-
attributes[
|
|
436
|
-
attributes[
|
|
331
|
+
attributes[`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_TYPE}`] = "image";
|
|
332
|
+
attributes[`${contentsIndexPrefix}${SemanticConventions.MESSAGE_CONTENT_IMAGE}`] = part.image_url.url;
|
|
437
333
|
}
|
|
438
334
|
});
|
|
439
335
|
}
|
|
@@ -443,16 +339,16 @@ function getChatCompletionInputMessageAttributes(message) {
|
|
|
443
339
|
break;
|
|
444
340
|
case "assistant":
|
|
445
341
|
if (message.tool_calls) {
|
|
446
|
-
message.tool_calls.forEach(
|
|
447
|
-
|
|
342
|
+
message.tool_calls.forEach((toolCall, index) => {
|
|
343
|
+
const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
|
|
448
344
|
// Add the tool call id if it exists
|
|
449
345
|
if (toolCall.id) {
|
|
450
|
-
attributes[
|
|
346
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`] = toolCall.id;
|
|
451
347
|
}
|
|
452
348
|
// Make sure the tool call has a function
|
|
453
349
|
if (toolCall.function) {
|
|
454
|
-
attributes[
|
|
455
|
-
attributes[
|
|
350
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_FUNCTION_NAME}`] = toolCall.function.name;
|
|
351
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_FUNCTION_ARGUMENTS_JSON}`] = toolCall.function.arguments;
|
|
456
352
|
}
|
|
457
353
|
});
|
|
458
354
|
}
|
|
@@ -462,13 +358,16 @@ function getChatCompletionInputMessageAttributes(message) {
|
|
|
462
358
|
break;
|
|
463
359
|
case "tool":
|
|
464
360
|
if (message.tool_call_id) {
|
|
465
|
-
attributes[
|
|
361
|
+
attributes[`${SemanticConventions.MESSAGE_TOOL_CALL_ID}`] =
|
|
466
362
|
message.tool_call_id;
|
|
467
363
|
}
|
|
468
364
|
break;
|
|
469
365
|
case "system":
|
|
470
366
|
// There's nothing to add for the system. Content is captured above
|
|
471
367
|
break;
|
|
368
|
+
case "developer":
|
|
369
|
+
// There's nothing to add for the developer. Content is captured above
|
|
370
|
+
break;
|
|
472
371
|
default:
|
|
473
372
|
assertUnreachable(role);
|
|
474
373
|
break;
|
|
@@ -479,22 +378,21 @@ function getChatCompletionInputMessageAttributes(message) {
|
|
|
479
378
|
* Converts the body of a completions request to input attributes
|
|
480
379
|
*/
|
|
481
380
|
function getCompletionInputValueAndMimeType(body) {
|
|
482
|
-
var _a, _b;
|
|
483
381
|
if (typeof body.prompt === "string") {
|
|
484
|
-
return
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
382
|
+
return {
|
|
383
|
+
[SemanticConventions.INPUT_VALUE]: body.prompt,
|
|
384
|
+
[SemanticConventions.INPUT_MIME_TYPE]: MimeType.TEXT,
|
|
385
|
+
};
|
|
488
386
|
}
|
|
489
387
|
else if (isPromptStringArray(body.prompt)) {
|
|
490
|
-
|
|
388
|
+
const prompt = body.prompt[0]; // Only single prompts are currently supported
|
|
491
389
|
if (prompt === undefined) {
|
|
492
390
|
return {};
|
|
493
391
|
}
|
|
494
|
-
return
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
392
|
+
return {
|
|
393
|
+
[SemanticConventions.INPUT_VALUE]: prompt,
|
|
394
|
+
[SemanticConventions.INPUT_MIME_TYPE]: MimeType.TEXT,
|
|
395
|
+
};
|
|
498
396
|
}
|
|
499
397
|
// Other cases in which the prompt is a token or array of tokens are currently unsupported
|
|
500
398
|
return {};
|
|
@@ -503,13 +401,12 @@ function getCompletionInputValueAndMimeType(body) {
|
|
|
503
401
|
* Get usage attributes
|
|
504
402
|
*/
|
|
505
403
|
function getUsageAttributes(completion) {
|
|
506
|
-
var _a;
|
|
507
404
|
if (completion.usage) {
|
|
508
|
-
return
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
405
|
+
return {
|
|
406
|
+
[SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]: completion.usage.completion_tokens,
|
|
407
|
+
[SemanticConventions.LLM_TOKEN_COUNT_PROMPT]: completion.usage.prompt_tokens,
|
|
408
|
+
[SemanticConventions.LLM_TOKEN_COUNT_TOTAL]: completion.usage.total_tokens,
|
|
409
|
+
};
|
|
513
410
|
}
|
|
514
411
|
return {};
|
|
515
412
|
}
|
|
@@ -518,46 +415,34 @@ function getUsageAttributes(completion) {
|
|
|
518
415
|
*/
|
|
519
416
|
function getChatCompletionLLMOutputMessagesAttributes(chatCompletion) {
|
|
520
417
|
// Right now support just the first choice
|
|
521
|
-
|
|
418
|
+
const choice = chatCompletion.choices[0];
|
|
522
419
|
if (!choice) {
|
|
523
420
|
return {};
|
|
524
421
|
}
|
|
525
|
-
return [choice.message].reduce(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
for (var _b = __values(Object.entries(messageAttributes)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
532
|
-
var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
|
|
533
|
-
acc["".concat(indexPrefix).concat(key)] = value;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
537
|
-
finally {
|
|
538
|
-
try {
|
|
539
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
540
|
-
}
|
|
541
|
-
finally { if (e_2) throw e_2.error; }
|
|
422
|
+
return [choice.message].reduce((acc, message, index) => {
|
|
423
|
+
const indexPrefix = `${SemanticConventions.LLM_OUTPUT_MESSAGES}.${index}.`;
|
|
424
|
+
const messageAttributes = getChatCompletionOutputMessageAttributes(message);
|
|
425
|
+
// Flatten the attributes on the index prefix
|
|
426
|
+
for (const [key, value] of Object.entries(messageAttributes)) {
|
|
427
|
+
acc[`${indexPrefix}${key}`] = value;
|
|
542
428
|
}
|
|
543
429
|
return acc;
|
|
544
430
|
}, {});
|
|
545
431
|
}
|
|
546
432
|
function getChatCompletionOutputMessageAttributes(message) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
_a);
|
|
433
|
+
const role = message.role;
|
|
434
|
+
const attributes = {
|
|
435
|
+
[SemanticConventions.MESSAGE_ROLE]: role,
|
|
436
|
+
};
|
|
552
437
|
if (message.content) {
|
|
553
438
|
attributes[SemanticConventions.MESSAGE_CONTENT] = message.content;
|
|
554
439
|
}
|
|
555
440
|
if (message.tool_calls) {
|
|
556
|
-
message.tool_calls.forEach(
|
|
557
|
-
|
|
441
|
+
message.tool_calls.forEach((toolCall, index) => {
|
|
442
|
+
const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
|
|
558
443
|
// Add the tool call id if it exists
|
|
559
444
|
if (toolCall.id) {
|
|
560
|
-
attributes[
|
|
445
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`] = toolCall.id;
|
|
561
446
|
}
|
|
562
447
|
// Double check that the tool call has a function
|
|
563
448
|
// NB: OpenAI only supports tool calls with functions right now but this may change
|
|
@@ -580,33 +465,31 @@ function getChatCompletionOutputMessageAttributes(message) {
|
|
|
580
465
|
* Converts the completion result to output attributes
|
|
581
466
|
*/
|
|
582
467
|
function getCompletionOutputValueAndMimeType(completion) {
|
|
583
|
-
var _a;
|
|
584
468
|
// Right now support just the first choice
|
|
585
|
-
|
|
469
|
+
const choice = completion.choices[0];
|
|
586
470
|
if (!choice) {
|
|
587
471
|
return {};
|
|
588
472
|
}
|
|
589
|
-
return
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
473
|
+
return {
|
|
474
|
+
[SemanticConventions.OUTPUT_VALUE]: String(choice.text),
|
|
475
|
+
[SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.TEXT,
|
|
476
|
+
};
|
|
593
477
|
}
|
|
594
478
|
/**
|
|
595
479
|
* Converts the embedding result payload to embedding attributes
|
|
596
480
|
*/
|
|
597
481
|
function getEmbeddingTextAttributes(request) {
|
|
598
|
-
var _a;
|
|
599
482
|
if (typeof request.input === "string") {
|
|
600
|
-
return
|
|
601
|
-
|
|
602
|
-
|
|
483
|
+
return {
|
|
484
|
+
[`${SemanticConventions.EMBEDDING_EMBEDDINGS}.0.${SemanticConventions.EMBEDDING_TEXT}`]: request.input,
|
|
485
|
+
};
|
|
603
486
|
}
|
|
604
487
|
else if (Array.isArray(request.input) &&
|
|
605
488
|
request.input.length > 0 &&
|
|
606
489
|
typeof request.input[0] === "string") {
|
|
607
|
-
return request.input.reduce(
|
|
608
|
-
|
|
609
|
-
acc[
|
|
490
|
+
return request.input.reduce((acc, input, index) => {
|
|
491
|
+
const indexPrefix = `${SemanticConventions.EMBEDDING_EMBEDDINGS}.${index}.`;
|
|
492
|
+
acc[`${indexPrefix}${SemanticConventions.EMBEDDING_TEXT}`] = input;
|
|
610
493
|
return acc;
|
|
611
494
|
}, {});
|
|
612
495
|
}
|
|
@@ -617,9 +500,9 @@ function getEmbeddingTextAttributes(request) {
|
|
|
617
500
|
* Converts the embedding result payload to embedding attributes
|
|
618
501
|
*/
|
|
619
502
|
function getEmbeddingEmbeddingsAttributes(response) {
|
|
620
|
-
return response.data.reduce(
|
|
621
|
-
|
|
622
|
-
acc[
|
|
503
|
+
return response.data.reduce((acc, embedding, index) => {
|
|
504
|
+
const indexPrefix = `${SemanticConventions.EMBEDDING_EMBEDDINGS}.${index}.`;
|
|
505
|
+
acc[`${indexPrefix}${SemanticConventions.EMBEDDING_VECTOR}`] =
|
|
623
506
|
embedding.embedding;
|
|
624
507
|
return acc;
|
|
625
508
|
}, {});
|
|
@@ -627,103 +510,47 @@ function getEmbeddingEmbeddingsAttributes(response) {
|
|
|
627
510
|
/**
|
|
628
511
|
* Consumes the stream chunks and adds them to the span
|
|
629
512
|
*/
|
|
630
|
-
function consumeChatCompletionStreamChunks(stream, span) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
chunk = _o;
|
|
652
|
-
if (chunk.choices.length <= 0) {
|
|
653
|
-
return [3 /*break*/, 4];
|
|
654
|
-
}
|
|
655
|
-
choice = chunk.choices[0];
|
|
656
|
-
if (choice.delta.content) {
|
|
657
|
-
streamResponse += choice.delta.content;
|
|
658
|
-
}
|
|
659
|
-
toolAndFunctionCallAttributesDiff = getToolAndFunctionCallAttributesFromStreamChunk(chunk);
|
|
660
|
-
try {
|
|
661
|
-
for (_a = (e_4 = void 0, __values(Object.entries(toolAndFunctionCallAttributesDiff))), _b = _a.next(); !_b.done; _b = _a.next()) {
|
|
662
|
-
_c = __read(_b.value, 2), key = _c[0], value = _c[1];
|
|
663
|
-
if (isString(toolAndFunctionCallAttributes[key]) && isString(value)) {
|
|
664
|
-
toolAndFunctionCallAttributes[key] += value;
|
|
665
|
-
}
|
|
666
|
-
else if (isString(value)) {
|
|
667
|
-
toolAndFunctionCallAttributes[key] = value;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
672
|
-
finally {
|
|
673
|
-
try {
|
|
674
|
-
if (_b && !_b.done && (_g = _a.return)) _g.call(_a);
|
|
675
|
-
}
|
|
676
|
-
finally { if (e_4) throw e_4.error; }
|
|
677
|
-
}
|
|
678
|
-
_p.label = 4;
|
|
679
|
-
case 4:
|
|
680
|
-
_k = true;
|
|
681
|
-
return [3 /*break*/, 2];
|
|
682
|
-
case 5: return [3 /*break*/, 12];
|
|
683
|
-
case 6:
|
|
684
|
-
e_3_1 = _p.sent();
|
|
685
|
-
e_3 = { error: e_3_1 };
|
|
686
|
-
return [3 /*break*/, 12];
|
|
687
|
-
case 7:
|
|
688
|
-
_p.trys.push([7, , 10, 11]);
|
|
689
|
-
if (!(!_k && !_l && (_m = stream_1.return))) return [3 /*break*/, 9];
|
|
690
|
-
return [4 /*yield*/, _m.call(stream_1)];
|
|
691
|
-
case 8:
|
|
692
|
-
_p.sent();
|
|
693
|
-
_p.label = 9;
|
|
694
|
-
case 9: return [3 /*break*/, 11];
|
|
695
|
-
case 10:
|
|
696
|
-
if (e_3) throw e_3.error;
|
|
697
|
-
return [7 /*endfinally*/];
|
|
698
|
-
case 11: return [7 /*endfinally*/];
|
|
699
|
-
case 12:
|
|
700
|
-
messageIndexPrefix = "".concat(SemanticConventions.LLM_OUTPUT_MESSAGES, ".0.");
|
|
701
|
-
attributes = (_h = {},
|
|
702
|
-
_h[SemanticConventions.OUTPUT_VALUE] = streamResponse,
|
|
703
|
-
_h[SemanticConventions.OUTPUT_MIME_TYPE] = MimeType.TEXT,
|
|
704
|
-
_h["".concat(messageIndexPrefix).concat(SemanticConventions.MESSAGE_CONTENT)] = streamResponse,
|
|
705
|
-
_h["".concat(messageIndexPrefix).concat(SemanticConventions.MESSAGE_ROLE)] = "assistant",
|
|
706
|
-
_h);
|
|
707
|
-
try {
|
|
708
|
-
// Add the tool and function call attributes
|
|
709
|
-
for (_d = __values(Object.entries(toolAndFunctionCallAttributes)), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
710
|
-
_f = __read(_e.value, 2), key = _f[0], value = _f[1];
|
|
711
|
-
attributes["".concat(messageIndexPrefix).concat(key)] = value;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
715
|
-
finally {
|
|
716
|
-
try {
|
|
717
|
-
if (_e && !_e.done && (_j = _d.return)) _j.call(_d);
|
|
718
|
-
}
|
|
719
|
-
finally { if (e_5) throw e_5.error; }
|
|
720
|
-
}
|
|
721
|
-
span.setAttributes(attributes);
|
|
722
|
-
span.end();
|
|
723
|
-
return [2 /*return*/];
|
|
513
|
+
async function consumeChatCompletionStreamChunks(stream, span) {
|
|
514
|
+
let streamResponse = "";
|
|
515
|
+
// Tool and function call attributes can also arrive in the stream
|
|
516
|
+
// NB: the tools and function calls arrive in partial diffs
|
|
517
|
+
// So the final tool and function calls need to be aggregated
|
|
518
|
+
// across chunks
|
|
519
|
+
const toolAndFunctionCallAttributes = {};
|
|
520
|
+
// The first message is for the assistant response so we start at 1
|
|
521
|
+
for await (const chunk of stream) {
|
|
522
|
+
if (chunk.choices.length <= 0) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
const choice = chunk.choices[0];
|
|
526
|
+
if (choice.delta.content) {
|
|
527
|
+
streamResponse += choice.delta.content;
|
|
528
|
+
}
|
|
529
|
+
// Accumulate the tool and function call attributes
|
|
530
|
+
const toolAndFunctionCallAttributesDiff = getToolAndFunctionCallAttributesFromStreamChunk(chunk);
|
|
531
|
+
for (const [key, value] of Object.entries(toolAndFunctionCallAttributesDiff)) {
|
|
532
|
+
if (isString(toolAndFunctionCallAttributes[key]) && isString(value)) {
|
|
533
|
+
toolAndFunctionCallAttributes[key] += value;
|
|
724
534
|
}
|
|
725
|
-
|
|
726
|
-
|
|
535
|
+
else if (isString(value)) {
|
|
536
|
+
toolAndFunctionCallAttributes[key] = value;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const messageIndexPrefix = `${SemanticConventions.LLM_OUTPUT_MESSAGES}.0.`;
|
|
541
|
+
// Append the attributes to the span as a message
|
|
542
|
+
const attributes = {
|
|
543
|
+
[SemanticConventions.OUTPUT_VALUE]: streamResponse,
|
|
544
|
+
[SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.TEXT,
|
|
545
|
+
[`${messageIndexPrefix}${SemanticConventions.MESSAGE_CONTENT}`]: streamResponse,
|
|
546
|
+
[`${messageIndexPrefix}${SemanticConventions.MESSAGE_ROLE}`]: "assistant",
|
|
547
|
+
};
|
|
548
|
+
// Add the tool and function call attributes
|
|
549
|
+
for (const [key, value] of Object.entries(toolAndFunctionCallAttributes)) {
|
|
550
|
+
attributes[`${messageIndexPrefix}${key}`] = value;
|
|
551
|
+
}
|
|
552
|
+
span.setAttributes(attributes);
|
|
553
|
+
span.end();
|
|
727
554
|
}
|
|
728
555
|
/**
|
|
729
556
|
* Extracts the semantic attributes from the stream chunk for tool_calls and function_calls
|
|
@@ -732,14 +559,14 @@ function getToolAndFunctionCallAttributesFromStreamChunk(chunk) {
|
|
|
732
559
|
if (chunk.choices.length <= 0) {
|
|
733
560
|
return {};
|
|
734
561
|
}
|
|
735
|
-
|
|
736
|
-
|
|
562
|
+
const choice = chunk.choices[0];
|
|
563
|
+
const attributes = {};
|
|
737
564
|
if (choice.delta.tool_calls) {
|
|
738
|
-
choice.delta.tool_calls.forEach(
|
|
739
|
-
|
|
565
|
+
choice.delta.tool_calls.forEach((toolCall, index) => {
|
|
566
|
+
const toolCallIndexPrefix = `${SemanticConventions.MESSAGE_TOOL_CALLS}.${index}.`;
|
|
740
567
|
// Add the tool call id if it exists
|
|
741
568
|
if (toolCall.id) {
|
|
742
|
-
attributes[
|
|
569
|
+
attributes[`${toolCallIndexPrefix}${SemanticConventions.TOOL_CALL_ID}`] = toolCall.id;
|
|
743
570
|
}
|
|
744
571
|
// Double check that the tool call has a function
|
|
745
572
|
// NB: OpenAI only supports tool calls with functions right now but this may change
|