@arizeai/openinference-instrumentation-openai 1.1.1 → 2.0.0
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 +226 -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/tsconfig.esnext.tsbuildinfo +1 -1
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +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 +12 -5
- 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
package/dist/test/openai.test.js
DELETED
|
@@ -1,1057 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
35
|
-
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
36
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
37
|
-
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
38
|
-
return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
39
|
-
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
40
|
-
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
|
|
41
|
-
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
42
|
-
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
43
|
-
function fulfill(value) { resume("next", value); }
|
|
44
|
-
function reject(value) { resume("throw", value); }
|
|
45
|
-
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
46
|
-
};
|
|
47
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
48
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
49
|
-
var m = o[Symbol.asyncIterator], i;
|
|
50
|
-
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);
|
|
51
|
-
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); }); }; }
|
|
52
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
53
|
-
};
|
|
54
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
-
const src_1 = require("../src");
|
|
56
|
-
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
|
|
57
|
-
const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
|
|
58
|
-
const core_1 = require("@opentelemetry/core");
|
|
59
|
-
const api_1 = require("@opentelemetry/api");
|
|
60
|
-
const OpenAI = __importStar(require("openai"));
|
|
61
|
-
const streaming_1 = require("openai/streaming");
|
|
62
|
-
const openinference_core_1 = require("@arizeai/openinference-core");
|
|
63
|
-
// Function tools
|
|
64
|
-
function getCurrentLocation() {
|
|
65
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
-
return "Boston"; // Simulate lookup
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
function getWeather(_args) {
|
|
70
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
-
return { temperature: 52, precipitation: "rainy" };
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
const memoryExporter = new sdk_trace_base_1.InMemorySpanExporter();
|
|
75
|
-
describe("OpenAIInstrumentation", () => {
|
|
76
|
-
const tracerProvider = new sdk_trace_node_1.NodeTracerProvider();
|
|
77
|
-
tracerProvider.register();
|
|
78
|
-
const instrumentation = new src_1.OpenAIInstrumentation();
|
|
79
|
-
instrumentation.disable();
|
|
80
|
-
let openai;
|
|
81
|
-
instrumentation.setTracerProvider(tracerProvider);
|
|
82
|
-
tracerProvider.addSpanProcessor(new sdk_trace_base_1.SimpleSpanProcessor(memoryExporter));
|
|
83
|
-
// @ts-expect-error the moduleExports property is private. This is needed to make the test work with auto-mocking
|
|
84
|
-
instrumentation._modules[0].moduleExports = OpenAI;
|
|
85
|
-
beforeAll(() => {
|
|
86
|
-
instrumentation.enable();
|
|
87
|
-
openai = new OpenAI.OpenAI({
|
|
88
|
-
apiKey: "fake-api-key",
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
afterAll(() => {
|
|
92
|
-
instrumentation.disable();
|
|
93
|
-
});
|
|
94
|
-
beforeEach(() => {
|
|
95
|
-
memoryExporter.reset();
|
|
96
|
-
});
|
|
97
|
-
afterEach(() => {
|
|
98
|
-
jest.clearAllMocks();
|
|
99
|
-
});
|
|
100
|
-
it("is patched", () => {
|
|
101
|
-
expect(OpenAI.openInferencePatched).toBe(true);
|
|
102
|
-
expect((0, src_1.isPatched)()).toBe(true);
|
|
103
|
-
});
|
|
104
|
-
it("sets a patched flag correctly to track whether or not openai is instrumented", () => {
|
|
105
|
-
instrumentation.disable();
|
|
106
|
-
expect((0, src_1.isPatched)()).toBe(false);
|
|
107
|
-
instrumentation.enable();
|
|
108
|
-
expect((0, src_1.isPatched)()).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
it("creates a span for chat completions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
111
|
-
const response = {
|
|
112
|
-
id: "chatcmpl-8adq9JloOzNZ9TyuzrKyLpGXexh6p",
|
|
113
|
-
object: "chat.completion",
|
|
114
|
-
created: 1703743645,
|
|
115
|
-
model: "gpt-3.5-turbo-0613",
|
|
116
|
-
choices: [
|
|
117
|
-
{
|
|
118
|
-
index: 0,
|
|
119
|
-
message: {
|
|
120
|
-
role: "assistant",
|
|
121
|
-
content: "This is a test.",
|
|
122
|
-
},
|
|
123
|
-
logprobs: null,
|
|
124
|
-
finish_reason: "stop",
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
usage: {
|
|
128
|
-
prompt_tokens: 12,
|
|
129
|
-
completion_tokens: 5,
|
|
130
|
-
total_tokens: 17,
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
// Mock out the chat completions endpoint
|
|
134
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
135
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
136
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
137
|
-
return response;
|
|
138
|
-
}));
|
|
139
|
-
yield openai.chat.completions.create({
|
|
140
|
-
messages: [{ role: "user", content: "Say this is a test" }],
|
|
141
|
-
model: "gpt-3.5-turbo",
|
|
142
|
-
});
|
|
143
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
144
|
-
expect(spans.length).toBe(1);
|
|
145
|
-
const span = spans[0];
|
|
146
|
-
expect(span.name).toBe("OpenAI Chat Completions");
|
|
147
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
148
|
-
{
|
|
149
|
-
"input.mime_type": "application/json",
|
|
150
|
-
"input.value": "{"messages":[{"role":"user","content":"Say this is a test"}],"model":"gpt-3.5-turbo"}",
|
|
151
|
-
"llm.input_messages.0.message.content": "Say this is a test",
|
|
152
|
-
"llm.input_messages.0.message.role": "user",
|
|
153
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo"}",
|
|
154
|
-
"llm.model_name": "gpt-3.5-turbo-0613",
|
|
155
|
-
"llm.output_messages.0.message.content": "This is a test.",
|
|
156
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
157
|
-
"llm.provider": "openai",
|
|
158
|
-
"llm.system": "openai",
|
|
159
|
-
"llm.token_count.completion": 5,
|
|
160
|
-
"llm.token_count.prompt": 12,
|
|
161
|
-
"llm.token_count.total": 17,
|
|
162
|
-
"openinference.span.kind": "LLM",
|
|
163
|
-
"output.mime_type": "application/json",
|
|
164
|
-
"output.value": "{"id":"chatcmpl-8adq9JloOzNZ9TyuzrKyLpGXexh6p","object":"chat.completion","created":1703743645,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"message":{"role":"assistant","content":"This is a test."},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":5,"total_tokens":17}}",
|
|
165
|
-
}
|
|
166
|
-
`);
|
|
167
|
-
}));
|
|
168
|
-
it("creates a span for completions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
169
|
-
const response = {
|
|
170
|
-
id: "cmpl-8fZu1H3VijJUWev9asnxaYyQvJTC9",
|
|
171
|
-
object: "text_completion",
|
|
172
|
-
created: 1704920149,
|
|
173
|
-
model: "gpt-3.5-turbo-instruct",
|
|
174
|
-
choices: [
|
|
175
|
-
{
|
|
176
|
-
text: "This is a test",
|
|
177
|
-
index: 0,
|
|
178
|
-
logprobs: null,
|
|
179
|
-
finish_reason: "stop",
|
|
180
|
-
},
|
|
181
|
-
],
|
|
182
|
-
usage: { prompt_tokens: 12, completion_tokens: 5, total_tokens: 17 },
|
|
183
|
-
};
|
|
184
|
-
// Mock out the completions endpoint
|
|
185
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
186
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
187
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
188
|
-
return response;
|
|
189
|
-
}));
|
|
190
|
-
yield openai.completions.create({
|
|
191
|
-
prompt: "Say this is a test",
|
|
192
|
-
model: "gpt-3.5-turbo-instruct",
|
|
193
|
-
});
|
|
194
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
195
|
-
expect(spans.length).toBe(1);
|
|
196
|
-
const span = spans[0];
|
|
197
|
-
expect(span.name).toBe("OpenAI Completions");
|
|
198
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
199
|
-
{
|
|
200
|
-
"input.mime_type": "text/plain",
|
|
201
|
-
"input.value": "Say this is a test",
|
|
202
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo-instruct"}",
|
|
203
|
-
"llm.model_name": "gpt-3.5-turbo-instruct",
|
|
204
|
-
"llm.provider": "openai",
|
|
205
|
-
"llm.system": "openai",
|
|
206
|
-
"llm.token_count.completion": 5,
|
|
207
|
-
"llm.token_count.prompt": 12,
|
|
208
|
-
"llm.token_count.total": 17,
|
|
209
|
-
"openinference.span.kind": "LLM",
|
|
210
|
-
"output.mime_type": "text/plain",
|
|
211
|
-
"output.value": "This is a test",
|
|
212
|
-
}
|
|
213
|
-
`);
|
|
214
|
-
}));
|
|
215
|
-
it("creates a span for embedding create", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
216
|
-
const response = {
|
|
217
|
-
object: "list",
|
|
218
|
-
data: [{ object: "embedding", index: 0, embedding: [1, 2, 3] }],
|
|
219
|
-
};
|
|
220
|
-
// Mock out the embedding create endpoint
|
|
221
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
222
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
223
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
224
|
-
return response;
|
|
225
|
-
}));
|
|
226
|
-
yield openai.embeddings.create({
|
|
227
|
-
input: "A happy moment",
|
|
228
|
-
model: "text-embedding-ada-002",
|
|
229
|
-
});
|
|
230
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
231
|
-
expect(spans.length).toBe(1);
|
|
232
|
-
const span = spans[0];
|
|
233
|
-
expect(span.name).toBe("OpenAI Embeddings");
|
|
234
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
235
|
-
{
|
|
236
|
-
"embedding.embeddings.0.embedding.text": "A happy moment",
|
|
237
|
-
"embedding.embeddings.0.embedding.vector": [
|
|
238
|
-
1,
|
|
239
|
-
2,
|
|
240
|
-
3,
|
|
241
|
-
],
|
|
242
|
-
"embedding.model_name": "text-embedding-ada-002",
|
|
243
|
-
"input.mime_type": "text/plain",
|
|
244
|
-
"input.value": "A happy moment",
|
|
245
|
-
"openinference.span.kind": "EMBEDDING",
|
|
246
|
-
}
|
|
247
|
-
`);
|
|
248
|
-
}));
|
|
249
|
-
it("can handle streaming responses", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
250
|
-
var _a, e_1, _b, _c;
|
|
251
|
-
// Mock out the post endpoint to return a stream
|
|
252
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
253
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
254
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
255
|
-
const iterator = () => (function () {
|
|
256
|
-
return __asyncGenerator(this, arguments, function* () {
|
|
257
|
-
yield yield __await({ choices: [{ delta: { content: "This is " } }] });
|
|
258
|
-
yield yield __await({ choices: [{ delta: { content: "a test." } }] });
|
|
259
|
-
yield yield __await({ choices: [{ delta: { finish_reason: "stop" } }] });
|
|
260
|
-
});
|
|
261
|
-
})();
|
|
262
|
-
const controller = new AbortController();
|
|
263
|
-
return new streaming_1.Stream(iterator, controller);
|
|
264
|
-
}));
|
|
265
|
-
const stream = yield openai.chat.completions.create({
|
|
266
|
-
messages: [{ role: "user", content: "Say this is a test" }],
|
|
267
|
-
model: "gpt-3.5-turbo",
|
|
268
|
-
stream: true,
|
|
269
|
-
});
|
|
270
|
-
let response = "";
|
|
271
|
-
try {
|
|
272
|
-
for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
|
|
273
|
-
_c = stream_1_1.value;
|
|
274
|
-
_d = false;
|
|
275
|
-
const chunk = _c;
|
|
276
|
-
if (chunk.choices[0].delta.content)
|
|
277
|
-
response += chunk.choices[0].delta.content;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
281
|
-
finally {
|
|
282
|
-
try {
|
|
283
|
-
if (!_d && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
|
|
284
|
-
}
|
|
285
|
-
finally { if (e_1) throw e_1.error; }
|
|
286
|
-
}
|
|
287
|
-
expect(response).toBe("This is a test.");
|
|
288
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
289
|
-
expect(spans.length).toBe(1);
|
|
290
|
-
const span = spans[0];
|
|
291
|
-
expect(span.name).toBe("OpenAI Chat Completions");
|
|
292
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
293
|
-
{
|
|
294
|
-
"input.mime_type": "application/json",
|
|
295
|
-
"input.value": "{"messages":[{"role":"user","content":"Say this is a test"}],"model":"gpt-3.5-turbo","stream":true}",
|
|
296
|
-
"llm.input_messages.0.message.content": "Say this is a test",
|
|
297
|
-
"llm.input_messages.0.message.role": "user",
|
|
298
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo","stream":true}",
|
|
299
|
-
"llm.model_name": "gpt-3.5-turbo",
|
|
300
|
-
"llm.output_messages.0.message.content": "This is a test.",
|
|
301
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
302
|
-
"llm.provider": "openai",
|
|
303
|
-
"llm.system": "openai",
|
|
304
|
-
"openinference.span.kind": "LLM",
|
|
305
|
-
"output.mime_type": "text/plain",
|
|
306
|
-
"output.value": "This is a test.",
|
|
307
|
-
}
|
|
308
|
-
`);
|
|
309
|
-
}));
|
|
310
|
-
it("should capture tool calls", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
311
|
-
// Mock out the embedding create endpoint
|
|
312
|
-
const response1 = {
|
|
313
|
-
id: "chatcmpl-8hhqZDFTRD0vzExhqWnMLE7viVl7E",
|
|
314
|
-
object: "chat.completion",
|
|
315
|
-
created: 1705427343,
|
|
316
|
-
model: "gpt-3.5-turbo-0613",
|
|
317
|
-
choices: [
|
|
318
|
-
{
|
|
319
|
-
index: 0,
|
|
320
|
-
message: {
|
|
321
|
-
role: "assistant",
|
|
322
|
-
content: null,
|
|
323
|
-
tool_calls: [
|
|
324
|
-
{
|
|
325
|
-
id: "call_5ERYvu4iTGSvDlcDQjDP3g3J",
|
|
326
|
-
type: "function",
|
|
327
|
-
function: { name: "getCurrentLocation", arguments: "{}" },
|
|
328
|
-
},
|
|
329
|
-
],
|
|
330
|
-
},
|
|
331
|
-
logprobs: null,
|
|
332
|
-
finish_reason: "tool_calls",
|
|
333
|
-
},
|
|
334
|
-
],
|
|
335
|
-
usage: { prompt_tokens: 70, completion_tokens: 7, total_tokens: 77 },
|
|
336
|
-
system_fingerprint: null,
|
|
337
|
-
};
|
|
338
|
-
const response2 = {
|
|
339
|
-
id: "chatcmpl-8hhsP9eAplUFYB3mHUJxBkq7IwnjZ",
|
|
340
|
-
object: "chat.completion",
|
|
341
|
-
created: 1705427457,
|
|
342
|
-
model: "gpt-3.5-turbo-0613",
|
|
343
|
-
choices: [
|
|
344
|
-
{
|
|
345
|
-
index: 0,
|
|
346
|
-
message: {
|
|
347
|
-
role: "assistant",
|
|
348
|
-
content: null,
|
|
349
|
-
tool_calls: [
|
|
350
|
-
{
|
|
351
|
-
id: "call_0LCdYLkdRUt3rV3dawoIFHBf",
|
|
352
|
-
type: "function",
|
|
353
|
-
function: {
|
|
354
|
-
name: "getWeather",
|
|
355
|
-
arguments: '{\n "location": "Boston"\n}',
|
|
356
|
-
},
|
|
357
|
-
},
|
|
358
|
-
],
|
|
359
|
-
},
|
|
360
|
-
logprobs: null,
|
|
361
|
-
finish_reason: "tool_calls",
|
|
362
|
-
},
|
|
363
|
-
],
|
|
364
|
-
usage: { prompt_tokens: 86, completion_tokens: 15, total_tokens: 101 },
|
|
365
|
-
system_fingerprint: null,
|
|
366
|
-
};
|
|
367
|
-
const response3 = {
|
|
368
|
-
id: "chatcmpl-8hhtfzSD33tsG7XJiBg4F9MqnXKDp",
|
|
369
|
-
object: "chat.completion",
|
|
370
|
-
created: 1705427535,
|
|
371
|
-
model: "gpt-3.5-turbo-0613",
|
|
372
|
-
choices: [
|
|
373
|
-
{
|
|
374
|
-
index: 0,
|
|
375
|
-
message: {
|
|
376
|
-
role: "assistant",
|
|
377
|
-
content: "The weather in Boston this week is expected to be rainy with a temperature of 52 degrees.",
|
|
378
|
-
},
|
|
379
|
-
logprobs: null,
|
|
380
|
-
finish_reason: "stop",
|
|
381
|
-
},
|
|
382
|
-
],
|
|
383
|
-
usage: { prompt_tokens: 121, completion_tokens: 20, total_tokens: 141 },
|
|
384
|
-
system_fingerprint: null,
|
|
385
|
-
};
|
|
386
|
-
jest
|
|
387
|
-
.spyOn(openai, "post")
|
|
388
|
-
.mockImplementationOnce(
|
|
389
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
390
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
391
|
-
return response1;
|
|
392
|
-
}))
|
|
393
|
-
.mockImplementationOnce(
|
|
394
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
395
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
396
|
-
return response2;
|
|
397
|
-
}))
|
|
398
|
-
.mockImplementationOnce(
|
|
399
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
400
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
401
|
-
return response3;
|
|
402
|
-
}));
|
|
403
|
-
const messages = [];
|
|
404
|
-
const runner = openai.beta.chat.completions
|
|
405
|
-
.runTools({
|
|
406
|
-
model: "gpt-3.5-turbo",
|
|
407
|
-
messages: [{ role: "user", content: "How is the weather this week?" }],
|
|
408
|
-
tools: [
|
|
409
|
-
{
|
|
410
|
-
type: "function",
|
|
411
|
-
function: {
|
|
412
|
-
function: getCurrentLocation,
|
|
413
|
-
parameters: { type: "object", properties: {} },
|
|
414
|
-
description: "Get the current location of the user.",
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
type: "function",
|
|
419
|
-
function: {
|
|
420
|
-
function: getWeather,
|
|
421
|
-
parse: JSON.parse, // or use a validation library like zod for typesafe parsing.
|
|
422
|
-
description: "Get the weather for a location.",
|
|
423
|
-
parameters: {
|
|
424
|
-
type: "object",
|
|
425
|
-
properties: {
|
|
426
|
-
location: { type: "string" },
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
},
|
|
430
|
-
},
|
|
431
|
-
],
|
|
432
|
-
})
|
|
433
|
-
.on("message", (message) => messages.push(message));
|
|
434
|
-
const _finalContent = yield runner.finalContent();
|
|
435
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
436
|
-
expect(spans.length).toBe(3);
|
|
437
|
-
const [span1, span2, span3] = spans;
|
|
438
|
-
expect(span1.name).toBe("OpenAI Chat Completions");
|
|
439
|
-
expect(span1.attributes).toMatchInlineSnapshot(`
|
|
440
|
-
{
|
|
441
|
-
"input.mime_type": "application/json",
|
|
442
|
-
"input.value": "{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"How is the weather this week?"}],"tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}],"tool_choice":"auto","stream":false}",
|
|
443
|
-
"llm.input_messages.0.message.content": "How is the weather this week?",
|
|
444
|
-
"llm.input_messages.0.message.role": "user",
|
|
445
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo","tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}],"tool_choice":"auto","stream":false}",
|
|
446
|
-
"llm.model_name": "gpt-3.5-turbo-0613",
|
|
447
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
448
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": "{}",
|
|
449
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "getCurrentLocation",
|
|
450
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_5ERYvu4iTGSvDlcDQjDP3g3J",
|
|
451
|
-
"llm.provider": "openai",
|
|
452
|
-
"llm.system": "openai",
|
|
453
|
-
"llm.token_count.completion": 7,
|
|
454
|
-
"llm.token_count.prompt": 70,
|
|
455
|
-
"llm.token_count.total": 77,
|
|
456
|
-
"llm.tools.0.tool.json_schema": "{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}}",
|
|
457
|
-
"llm.tools.1.tool.json_schema": "{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}",
|
|
458
|
-
"openinference.span.kind": "LLM",
|
|
459
|
-
"output.mime_type": "application/json",
|
|
460
|
-
"output.value": "{"id":"chatcmpl-8hhqZDFTRD0vzExhqWnMLE7viVl7E","object":"chat.completion","created":1705427343,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"message":{"role":"assistant","content":null,"tool_calls":[{"id":"call_5ERYvu4iTGSvDlcDQjDP3g3J","type":"function","function":{"name":"getCurrentLocation","arguments":"{}"}}]},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":70,"completion_tokens":7,"total_tokens":77},"system_fingerprint":null}",
|
|
461
|
-
}
|
|
462
|
-
`);
|
|
463
|
-
expect(span2.name).toBe("OpenAI Chat Completions");
|
|
464
|
-
expect(span2.attributes).toMatchInlineSnapshot(`
|
|
465
|
-
{
|
|
466
|
-
"input.mime_type": "application/json",
|
|
467
|
-
"input.value": "{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"How is the weather this week?"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_5ERYvu4iTGSvDlcDQjDP3g3J","type":"function","function":{"name":"getCurrentLocation","arguments":"{}","parsed_arguments":null}}],"parsed":null},{"role":"tool","tool_call_id":"call_5ERYvu4iTGSvDlcDQjDP3g3J","content":"Boston"}],"tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}],"tool_choice":"auto","stream":false}",
|
|
468
|
-
"llm.input_messages.0.message.content": "How is the weather this week?",
|
|
469
|
-
"llm.input_messages.0.message.role": "user",
|
|
470
|
-
"llm.input_messages.1.message.role": "assistant",
|
|
471
|
-
"llm.input_messages.1.message.tool_calls.0.tool_call.function.arguments": "{}",
|
|
472
|
-
"llm.input_messages.1.message.tool_calls.0.tool_call.function.name": "getCurrentLocation",
|
|
473
|
-
"llm.input_messages.1.message.tool_calls.0.tool_call.id": "call_5ERYvu4iTGSvDlcDQjDP3g3J",
|
|
474
|
-
"llm.input_messages.2.message.content": "Boston",
|
|
475
|
-
"llm.input_messages.2.message.role": "tool",
|
|
476
|
-
"llm.input_messages.2.message.tool_call_id": "call_5ERYvu4iTGSvDlcDQjDP3g3J",
|
|
477
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo","tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}],"tool_choice":"auto","stream":false}",
|
|
478
|
-
"llm.model_name": "gpt-3.5-turbo-0613",
|
|
479
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
480
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": "{
|
|
481
|
-
"location": "Boston"
|
|
482
|
-
}",
|
|
483
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "getWeather",
|
|
484
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_0LCdYLkdRUt3rV3dawoIFHBf",
|
|
485
|
-
"llm.provider": "openai",
|
|
486
|
-
"llm.system": "openai",
|
|
487
|
-
"llm.token_count.completion": 15,
|
|
488
|
-
"llm.token_count.prompt": 86,
|
|
489
|
-
"llm.token_count.total": 101,
|
|
490
|
-
"llm.tools.0.tool.json_schema": "{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}}",
|
|
491
|
-
"llm.tools.1.tool.json_schema": "{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}",
|
|
492
|
-
"openinference.span.kind": "LLM",
|
|
493
|
-
"output.mime_type": "application/json",
|
|
494
|
-
"output.value": "{"id":"chatcmpl-8hhsP9eAplUFYB3mHUJxBkq7IwnjZ","object":"chat.completion","created":1705427457,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"message":{"role":"assistant","content":null,"tool_calls":[{"id":"call_0LCdYLkdRUt3rV3dawoIFHBf","type":"function","function":{"name":"getWeather","arguments":"{\\n \\"location\\": \\"Boston\\"\\n}"}}]},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":86,"completion_tokens":15,"total_tokens":101},"system_fingerprint":null}",
|
|
495
|
-
}
|
|
496
|
-
`);
|
|
497
|
-
expect(span3.name).toBe("OpenAI Chat Completions");
|
|
498
|
-
expect(span3.attributes).toMatchInlineSnapshot(`
|
|
499
|
-
{
|
|
500
|
-
"input.mime_type": "application/json",
|
|
501
|
-
"input.value": "{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"How is the weather this week?"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_5ERYvu4iTGSvDlcDQjDP3g3J","type":"function","function":{"name":"getCurrentLocation","arguments":"{}","parsed_arguments":null}}],"parsed":null},{"role":"tool","tool_call_id":"call_5ERYvu4iTGSvDlcDQjDP3g3J","content":"Boston"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_0LCdYLkdRUt3rV3dawoIFHBf","type":"function","function":{"name":"getWeather","arguments":"{\\n \\"location\\": \\"Boston\\"\\n}","parsed_arguments":null}}],"parsed":null},{"role":"tool","tool_call_id":"call_0LCdYLkdRUt3rV3dawoIFHBf","content":"{\\"temperature\\":52,\\"precipitation\\":\\"rainy\\"}"}],"tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}],"tool_choice":"auto","stream":false}",
|
|
502
|
-
"llm.input_messages.0.message.content": "How is the weather this week?",
|
|
503
|
-
"llm.input_messages.0.message.role": "user",
|
|
504
|
-
"llm.input_messages.1.message.role": "assistant",
|
|
505
|
-
"llm.input_messages.1.message.tool_calls.0.tool_call.function.arguments": "{}",
|
|
506
|
-
"llm.input_messages.1.message.tool_calls.0.tool_call.function.name": "getCurrentLocation",
|
|
507
|
-
"llm.input_messages.1.message.tool_calls.0.tool_call.id": "call_5ERYvu4iTGSvDlcDQjDP3g3J",
|
|
508
|
-
"llm.input_messages.2.message.content": "Boston",
|
|
509
|
-
"llm.input_messages.2.message.role": "tool",
|
|
510
|
-
"llm.input_messages.2.message.tool_call_id": "call_5ERYvu4iTGSvDlcDQjDP3g3J",
|
|
511
|
-
"llm.input_messages.3.message.role": "assistant",
|
|
512
|
-
"llm.input_messages.3.message.tool_calls.0.tool_call.function.arguments": "{
|
|
513
|
-
"location": "Boston"
|
|
514
|
-
}",
|
|
515
|
-
"llm.input_messages.3.message.tool_calls.0.tool_call.function.name": "getWeather",
|
|
516
|
-
"llm.input_messages.3.message.tool_calls.0.tool_call.id": "call_0LCdYLkdRUt3rV3dawoIFHBf",
|
|
517
|
-
"llm.input_messages.4.message.content": "{"temperature":52,"precipitation":"rainy"}",
|
|
518
|
-
"llm.input_messages.4.message.role": "tool",
|
|
519
|
-
"llm.input_messages.4.message.tool_call_id": "call_0LCdYLkdRUt3rV3dawoIFHBf",
|
|
520
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo","tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}],"tool_choice":"auto","stream":false}",
|
|
521
|
-
"llm.model_name": "gpt-3.5-turbo-0613",
|
|
522
|
-
"llm.output_messages.0.message.content": "The weather in Boston this week is expected to be rainy with a temperature of 52 degrees.",
|
|
523
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
524
|
-
"llm.provider": "openai",
|
|
525
|
-
"llm.system": "openai",
|
|
526
|
-
"llm.token_count.completion": 20,
|
|
527
|
-
"llm.token_count.prompt": 121,
|
|
528
|
-
"llm.token_count.total": 141,
|
|
529
|
-
"llm.tools.0.tool.json_schema": "{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}}",
|
|
530
|
-
"llm.tools.1.tool.json_schema": "{"type":"function","function":{"name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}}},"description":"Get the weather for a location."}}",
|
|
531
|
-
"openinference.span.kind": "LLM",
|
|
532
|
-
"output.mime_type": "application/json",
|
|
533
|
-
"output.value": "{"id":"chatcmpl-8hhtfzSD33tsG7XJiBg4F9MqnXKDp","object":"chat.completion","created":1705427535,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"message":{"role":"assistant","content":"The weather in Boston this week is expected to be rainy with a temperature of 52 degrees."},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":121,"completion_tokens":20,"total_tokens":141},"system_fingerprint":null}",
|
|
534
|
-
}
|
|
535
|
-
`);
|
|
536
|
-
}));
|
|
537
|
-
it("should capture tool calls with streaming", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
538
|
-
var _a, e_2, _b, _c;
|
|
539
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
540
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
541
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
542
|
-
const iterator = () => (function () {
|
|
543
|
-
return __asyncGenerator(this, arguments, function* () {
|
|
544
|
-
yield yield __await({
|
|
545
|
-
id: "chatcmpl-8iA39kCtuVHIVDr9AnBdJZjgSjNWL",
|
|
546
|
-
object: "chat.completion.chunk",
|
|
547
|
-
created: 1705535755,
|
|
548
|
-
model: "gpt-3.5-turbo-0613",
|
|
549
|
-
system_fingerprint: null,
|
|
550
|
-
choices: [
|
|
551
|
-
{
|
|
552
|
-
index: 0,
|
|
553
|
-
delta: {
|
|
554
|
-
role: "assistant",
|
|
555
|
-
content: null,
|
|
556
|
-
tool_calls: [
|
|
557
|
-
{
|
|
558
|
-
index: 0,
|
|
559
|
-
id: "call_PGkcUg2u6vYrCpTn0e9ofykY",
|
|
560
|
-
type: "function",
|
|
561
|
-
function: { name: "getWeather", arguments: "" },
|
|
562
|
-
},
|
|
563
|
-
],
|
|
564
|
-
},
|
|
565
|
-
logprobs: null,
|
|
566
|
-
finish_reason: null,
|
|
567
|
-
},
|
|
568
|
-
],
|
|
569
|
-
});
|
|
570
|
-
yield yield __await({
|
|
571
|
-
id: "chatcmpl-8iA39kCtuVHIVDr9AnBdJZjgSjNWL",
|
|
572
|
-
object: "chat.completion.chunk",
|
|
573
|
-
created: 1705535755,
|
|
574
|
-
model: "gpt-3.5-turbo-0613",
|
|
575
|
-
system_fingerprint: null,
|
|
576
|
-
choices: [
|
|
577
|
-
{
|
|
578
|
-
index: 0,
|
|
579
|
-
delta: {
|
|
580
|
-
tool_calls: [{ index: 0, function: { arguments: "{}" } }],
|
|
581
|
-
},
|
|
582
|
-
logprobs: null,
|
|
583
|
-
finish_reason: null,
|
|
584
|
-
},
|
|
585
|
-
],
|
|
586
|
-
});
|
|
587
|
-
yield yield __await({
|
|
588
|
-
id: "chatcmpl-8iA39kCtuVHIVDr9AnBdJZjgSjNWL",
|
|
589
|
-
object: "chat.completion.chunk",
|
|
590
|
-
created: 1705535755,
|
|
591
|
-
model: "gpt-3.5-turbo-0613",
|
|
592
|
-
system_fingerprint: null,
|
|
593
|
-
choices: [
|
|
594
|
-
{
|
|
595
|
-
index: 0,
|
|
596
|
-
delta: {},
|
|
597
|
-
logprobs: null,
|
|
598
|
-
finish_reason: "tool_calls",
|
|
599
|
-
},
|
|
600
|
-
],
|
|
601
|
-
});
|
|
602
|
-
});
|
|
603
|
-
})();
|
|
604
|
-
const controller = new AbortController();
|
|
605
|
-
return new streaming_1.Stream(iterator, controller);
|
|
606
|
-
}));
|
|
607
|
-
const stream = yield openai.chat.completions.create({
|
|
608
|
-
messages: [{ role: "user", content: "What's the weather today?" }],
|
|
609
|
-
model: "gpt-3.5-turbo",
|
|
610
|
-
tools: [
|
|
611
|
-
{
|
|
612
|
-
type: "function",
|
|
613
|
-
function: {
|
|
614
|
-
name: "getCurrentLocation",
|
|
615
|
-
parameters: { type: "object", properties: {} },
|
|
616
|
-
description: "Get the current location of the user.",
|
|
617
|
-
},
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
type: "function",
|
|
621
|
-
function: {
|
|
622
|
-
name: "getWeather",
|
|
623
|
-
description: "Get the weather for a location.",
|
|
624
|
-
parameters: {
|
|
625
|
-
type: "object",
|
|
626
|
-
properties: {
|
|
627
|
-
location: { type: "string" },
|
|
628
|
-
},
|
|
629
|
-
},
|
|
630
|
-
},
|
|
631
|
-
},
|
|
632
|
-
],
|
|
633
|
-
stream: true,
|
|
634
|
-
});
|
|
635
|
-
let response = "";
|
|
636
|
-
try {
|
|
637
|
-
for (var _d = true, stream_2 = __asyncValues(stream), stream_2_1; stream_2_1 = yield stream_2.next(), _a = stream_2_1.done, !_a; _d = true) {
|
|
638
|
-
_c = stream_2_1.value;
|
|
639
|
-
_d = false;
|
|
640
|
-
const chunk = _c;
|
|
641
|
-
if (chunk.choices[0].delta.content)
|
|
642
|
-
response += chunk.choices[0].delta.content;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
646
|
-
finally {
|
|
647
|
-
try {
|
|
648
|
-
if (!_d && !_a && (_b = stream_2.return)) yield _b.call(stream_2);
|
|
649
|
-
}
|
|
650
|
-
finally { if (e_2) throw e_2.error; }
|
|
651
|
-
}
|
|
652
|
-
// When a tool is called, the content is empty
|
|
653
|
-
expect(response).toBe("");
|
|
654
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
655
|
-
expect(spans.length).toBe(1);
|
|
656
|
-
const span = spans[0];
|
|
657
|
-
expect(span.name).toBe("OpenAI Chat Completions");
|
|
658
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
659
|
-
{
|
|
660
|
-
"input.mime_type": "application/json",
|
|
661
|
-
"input.value": "{"messages":[{"role":"user","content":"What's the weather today?"}],"model":"gpt-3.5-turbo","tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","description":"Get the weather for a location.","parameters":{"type":"object","properties":{"location":{"type":"string"}}}}}],"stream":true}",
|
|
662
|
-
"llm.input_messages.0.message.content": "What's the weather today?",
|
|
663
|
-
"llm.input_messages.0.message.role": "user",
|
|
664
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo","tools":[{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}},{"type":"function","function":{"name":"getWeather","description":"Get the weather for a location.","parameters":{"type":"object","properties":{"location":{"type":"string"}}}}}],"stream":true}",
|
|
665
|
-
"llm.model_name": "gpt-3.5-turbo",
|
|
666
|
-
"llm.output_messages.0.message.content": "",
|
|
667
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
668
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": "{}",
|
|
669
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "getWeather",
|
|
670
|
-
"llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_PGkcUg2u6vYrCpTn0e9ofykY",
|
|
671
|
-
"llm.provider": "openai",
|
|
672
|
-
"llm.system": "openai",
|
|
673
|
-
"llm.tools.0.tool.json_schema": "{"type":"function","function":{"name":"getCurrentLocation","parameters":{"type":"object","properties":{}},"description":"Get the current location of the user."}}",
|
|
674
|
-
"llm.tools.1.tool.json_schema": "{"type":"function","function":{"name":"getWeather","description":"Get the weather for a location.","parameters":{"type":"object","properties":{"location":{"type":"string"}}}}}",
|
|
675
|
-
"openinference.span.kind": "LLM",
|
|
676
|
-
"output.mime_type": "text/plain",
|
|
677
|
-
"output.value": "",
|
|
678
|
-
}
|
|
679
|
-
`);
|
|
680
|
-
}));
|
|
681
|
-
it("should capture a function call with streaming", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
682
|
-
var _a, e_3, _b, _c;
|
|
683
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
684
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
685
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
686
|
-
const iterator = () => (function () {
|
|
687
|
-
return __asyncGenerator(this, arguments, function* () {
|
|
688
|
-
yield yield __await({
|
|
689
|
-
id: "chatcmpl-8iA39kCtuVHIVDr9AnBdJZjgSjNWL",
|
|
690
|
-
object: "chat.completion.chunk",
|
|
691
|
-
created: 1705535755,
|
|
692
|
-
model: "gpt-3.5-turbo-0613",
|
|
693
|
-
system_fingerprint: null,
|
|
694
|
-
choices: [
|
|
695
|
-
{
|
|
696
|
-
index: 0,
|
|
697
|
-
delta: {
|
|
698
|
-
role: "assistant",
|
|
699
|
-
content: null,
|
|
700
|
-
function_call: { name: "getWeather", arguments: "" },
|
|
701
|
-
},
|
|
702
|
-
logprobs: null,
|
|
703
|
-
finish_reason: null,
|
|
704
|
-
},
|
|
705
|
-
],
|
|
706
|
-
});
|
|
707
|
-
yield yield __await({
|
|
708
|
-
id: "chatcmpl-8iA39kCtuVHIVDr9AnBdJZjgSjNWL",
|
|
709
|
-
object: "chat.completion.chunk",
|
|
710
|
-
created: 1705535755,
|
|
711
|
-
model: "gpt-3.5-turbo-0613",
|
|
712
|
-
system_fingerprint: null,
|
|
713
|
-
choices: [
|
|
714
|
-
{
|
|
715
|
-
index: 0,
|
|
716
|
-
delta: { function_call: { arguments: "{}" } },
|
|
717
|
-
logprobs: null,
|
|
718
|
-
finish_reason: null,
|
|
719
|
-
},
|
|
720
|
-
],
|
|
721
|
-
});
|
|
722
|
-
yield yield __await({
|
|
723
|
-
id: "chatcmpl-8iA39kCtuVHIVDr9AnBdJZjgSjNWL",
|
|
724
|
-
object: "chat.completion.chunk",
|
|
725
|
-
created: 1705535755,
|
|
726
|
-
model: "gpt-3.5-turbo-0613",
|
|
727
|
-
system_fingerprint: null,
|
|
728
|
-
choices: [
|
|
729
|
-
{
|
|
730
|
-
index: 0,
|
|
731
|
-
delta: {},
|
|
732
|
-
logprobs: null,
|
|
733
|
-
finish_reason: "function_call",
|
|
734
|
-
},
|
|
735
|
-
],
|
|
736
|
-
});
|
|
737
|
-
});
|
|
738
|
-
})();
|
|
739
|
-
const controller = new AbortController();
|
|
740
|
-
return new streaming_1.Stream(iterator, controller);
|
|
741
|
-
}));
|
|
742
|
-
const stream = yield openai.chat.completions.create({
|
|
743
|
-
messages: [{ role: "user", content: "What's the weather today?" }],
|
|
744
|
-
model: "gpt-3.5-turbo",
|
|
745
|
-
functions: [
|
|
746
|
-
{
|
|
747
|
-
name: "getWeather",
|
|
748
|
-
description: "Get the weather for a location.",
|
|
749
|
-
parameters: {
|
|
750
|
-
type: "object",
|
|
751
|
-
properties: {
|
|
752
|
-
location: { type: "string" },
|
|
753
|
-
},
|
|
754
|
-
},
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
name: "getCurrentLocation",
|
|
758
|
-
description: "Get the current location of the user.",
|
|
759
|
-
parameters: { type: "object", properties: {} },
|
|
760
|
-
},
|
|
761
|
-
],
|
|
762
|
-
stream: true,
|
|
763
|
-
});
|
|
764
|
-
let response = "";
|
|
765
|
-
try {
|
|
766
|
-
for (var _d = true, stream_3 = __asyncValues(stream), stream_3_1; stream_3_1 = yield stream_3.next(), _a = stream_3_1.done, !_a; _d = true) {
|
|
767
|
-
_c = stream_3_1.value;
|
|
768
|
-
_d = false;
|
|
769
|
-
const chunk = _c;
|
|
770
|
-
if (chunk.choices[0].delta.content)
|
|
771
|
-
response += chunk.choices[0].delta.content;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
775
|
-
finally {
|
|
776
|
-
try {
|
|
777
|
-
if (!_d && !_a && (_b = stream_3.return)) yield _b.call(stream_3);
|
|
778
|
-
}
|
|
779
|
-
finally { if (e_3) throw e_3.error; }
|
|
780
|
-
}
|
|
781
|
-
// When a tool is called, the content is empty
|
|
782
|
-
expect(response).toBe("");
|
|
783
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
784
|
-
expect(spans.length).toBe(1);
|
|
785
|
-
const span = spans[0];
|
|
786
|
-
expect(span.name).toBe("OpenAI Chat Completions");
|
|
787
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
788
|
-
{
|
|
789
|
-
"input.mime_type": "application/json",
|
|
790
|
-
"input.value": "{"messages":[{"role":"user","content":"What's the weather today?"}],"model":"gpt-3.5-turbo","functions":[{"name":"getWeather","description":"Get the weather for a location.","parameters":{"type":"object","properties":{"location":{"type":"string"}}}},{"name":"getCurrentLocation","description":"Get the current location of the user.","parameters":{"type":"object","properties":{}}}],"stream":true}",
|
|
791
|
-
"llm.input_messages.0.message.content": "What's the weather today?",
|
|
792
|
-
"llm.input_messages.0.message.role": "user",
|
|
793
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo","functions":[{"name":"getWeather","description":"Get the weather for a location.","parameters":{"type":"object","properties":{"location":{"type":"string"}}}},{"name":"getCurrentLocation","description":"Get the current location of the user.","parameters":{"type":"object","properties":{}}}],"stream":true}",
|
|
794
|
-
"llm.model_name": "gpt-3.5-turbo",
|
|
795
|
-
"llm.output_messages.0.message.content": "",
|
|
796
|
-
"llm.output_messages.0.message.function_call_arguments_json": "{}",
|
|
797
|
-
"llm.output_messages.0.message.function_call_name": "getWeather",
|
|
798
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
799
|
-
"llm.provider": "openai",
|
|
800
|
-
"llm.system": "openai",
|
|
801
|
-
"openinference.span.kind": "LLM",
|
|
802
|
-
"output.mime_type": "text/plain",
|
|
803
|
-
"output.value": "",
|
|
804
|
-
}
|
|
805
|
-
`);
|
|
806
|
-
}));
|
|
807
|
-
it("should not emit a span if tracing is suppressed", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
808
|
-
const response = {
|
|
809
|
-
id: "chatcmpl-8adq9JloOzNZ9TyuzrKyLpGXexh6p",
|
|
810
|
-
object: "chat.completion",
|
|
811
|
-
created: 1703743645,
|
|
812
|
-
model: "gpt-3.5-turbo-0613",
|
|
813
|
-
choices: [
|
|
814
|
-
{
|
|
815
|
-
index: 0,
|
|
816
|
-
message: {
|
|
817
|
-
role: "assistant",
|
|
818
|
-
content: "This is a test.",
|
|
819
|
-
},
|
|
820
|
-
logprobs: null,
|
|
821
|
-
finish_reason: "stop",
|
|
822
|
-
},
|
|
823
|
-
],
|
|
824
|
-
usage: {
|
|
825
|
-
prompt_tokens: 12,
|
|
826
|
-
completion_tokens: 5,
|
|
827
|
-
total_tokens: 17,
|
|
828
|
-
},
|
|
829
|
-
};
|
|
830
|
-
// Mock out the chat completions endpoint
|
|
831
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
832
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
833
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
834
|
-
return response;
|
|
835
|
-
}));
|
|
836
|
-
const _response = yield new Promise((resolve, _reject) => {
|
|
837
|
-
api_1.context.with((0, core_1.suppressTracing)(api_1.context.active()), () => {
|
|
838
|
-
resolve(openai.chat.completions.create({
|
|
839
|
-
messages: [{ role: "user", content: "Say this is a test" }],
|
|
840
|
-
model: "gpt-3.5-turbo",
|
|
841
|
-
}));
|
|
842
|
-
});
|
|
843
|
-
});
|
|
844
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
845
|
-
expect(spans.length).toBe(0);
|
|
846
|
-
}));
|
|
847
|
-
it("should capture image in request", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
848
|
-
const response = {
|
|
849
|
-
id: "chatcmpl-8adq9JloOzNZ9TyuzrKyLpGXexh6p",
|
|
850
|
-
object: "chat.completion",
|
|
851
|
-
created: 1703743645,
|
|
852
|
-
model: "gpt-3.5-turbo-0613",
|
|
853
|
-
choices: [
|
|
854
|
-
{
|
|
855
|
-
index: 0,
|
|
856
|
-
message: {
|
|
857
|
-
role: "assistant",
|
|
858
|
-
content: "This is a test.",
|
|
859
|
-
},
|
|
860
|
-
logprobs: null,
|
|
861
|
-
finish_reason: "stop",
|
|
862
|
-
},
|
|
863
|
-
],
|
|
864
|
-
usage: {
|
|
865
|
-
prompt_tokens: 12,
|
|
866
|
-
completion_tokens: 5,
|
|
867
|
-
total_tokens: 17,
|
|
868
|
-
},
|
|
869
|
-
};
|
|
870
|
-
// Mock out the chat completions endpoint
|
|
871
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
872
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
873
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
874
|
-
return response;
|
|
875
|
-
}));
|
|
876
|
-
yield openai.chat.completions.create({
|
|
877
|
-
messages: [
|
|
878
|
-
{
|
|
879
|
-
role: "user",
|
|
880
|
-
content: [
|
|
881
|
-
{ type: "text", text: "Say this is a test" },
|
|
882
|
-
{
|
|
883
|
-
type: "image_url",
|
|
884
|
-
image_url: {
|
|
885
|
-
url: "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",
|
|
886
|
-
},
|
|
887
|
-
},
|
|
888
|
-
],
|
|
889
|
-
},
|
|
890
|
-
],
|
|
891
|
-
model: "gpt-3.5-turbo",
|
|
892
|
-
});
|
|
893
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
894
|
-
expect(spans.length).toBe(1);
|
|
895
|
-
const span = spans[0];
|
|
896
|
-
expect(span.name).toBe("OpenAI Chat Completions");
|
|
897
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
898
|
-
{
|
|
899
|
-
"input.mime_type": "application/json",
|
|
900
|
-
"input.value": "{"messages":[{"role":"user","content":[{"type":"text","text":"Say this is a test"},{"type":"image_url","image_url":{"url":"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="}}]}],"model":"gpt-3.5-turbo"}",
|
|
901
|
-
"llm.input_messages.0.message.contents.0.message_content.text": "Say this is a test",
|
|
902
|
-
"llm.input_messages.0.message.contents.0.message_content.type": "text",
|
|
903
|
-
"llm.input_messages.0.message.contents.1.message_content.image": "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",
|
|
904
|
-
"llm.input_messages.0.message.contents.1.message_content.type": "image",
|
|
905
|
-
"llm.input_messages.0.message.role": "user",
|
|
906
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo"}",
|
|
907
|
-
"llm.model_name": "gpt-3.5-turbo-0613",
|
|
908
|
-
"llm.output_messages.0.message.content": "This is a test.",
|
|
909
|
-
"llm.output_messages.0.message.role": "assistant",
|
|
910
|
-
"llm.provider": "openai",
|
|
911
|
-
"llm.system": "openai",
|
|
912
|
-
"llm.token_count.completion": 5,
|
|
913
|
-
"llm.token_count.prompt": 12,
|
|
914
|
-
"llm.token_count.total": 17,
|
|
915
|
-
"openinference.span.kind": "LLM",
|
|
916
|
-
"output.mime_type": "application/json",
|
|
917
|
-
"output.value": "{"id":"chatcmpl-8adq9JloOzNZ9TyuzrKyLpGXexh6p","object":"chat.completion","created":1703743645,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"message":{"role":"assistant","content":"This is a test."},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":5,"total_tokens":17}}",
|
|
918
|
-
}
|
|
919
|
-
`);
|
|
920
|
-
}));
|
|
921
|
-
it("should capture context attributes and add them to spans", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
922
|
-
const response = {
|
|
923
|
-
id: "cmpl-8fZu1H3VijJUWev9asnxaYyQvJTC9",
|
|
924
|
-
object: "text_completion",
|
|
925
|
-
created: 1704920149,
|
|
926
|
-
model: "gpt-3.5-turbo-instruct",
|
|
927
|
-
choices: [
|
|
928
|
-
{
|
|
929
|
-
text: "This is a test",
|
|
930
|
-
index: 0,
|
|
931
|
-
logprobs: null,
|
|
932
|
-
finish_reason: "stop",
|
|
933
|
-
},
|
|
934
|
-
],
|
|
935
|
-
usage: { prompt_tokens: 12, completion_tokens: 5, total_tokens: 17 },
|
|
936
|
-
};
|
|
937
|
-
// Mock out the completions endpoint
|
|
938
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
939
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
940
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
941
|
-
return response;
|
|
942
|
-
}));
|
|
943
|
-
yield api_1.context.with((0, openinference_core_1.setSession)((0, openinference_core_1.setPromptTemplate)(api_1.context.active(), {
|
|
944
|
-
template: "hello {name}",
|
|
945
|
-
variables: { name: "world" },
|
|
946
|
-
version: "V1.0",
|
|
947
|
-
}), { sessionId: "session-id" }), () => __awaiter(void 0, void 0, void 0, function* () {
|
|
948
|
-
yield openai.completions.create({
|
|
949
|
-
prompt: "Say this is a test",
|
|
950
|
-
model: "gpt-3.5-turbo-instruct",
|
|
951
|
-
});
|
|
952
|
-
}));
|
|
953
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
954
|
-
expect(spans.length).toBe(1);
|
|
955
|
-
const span = spans[0];
|
|
956
|
-
expect(span.name).toBe("OpenAI Completions");
|
|
957
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
958
|
-
{
|
|
959
|
-
"input.mime_type": "text/plain",
|
|
960
|
-
"input.value": "Say this is a test",
|
|
961
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo-instruct"}",
|
|
962
|
-
"llm.model_name": "gpt-3.5-turbo-instruct",
|
|
963
|
-
"llm.prompt_template.template": "hello {name}",
|
|
964
|
-
"llm.prompt_template.variables": "{"name":"world"}",
|
|
965
|
-
"llm.prompt_template.version": "V1.0",
|
|
966
|
-
"llm.provider": "openai",
|
|
967
|
-
"llm.system": "openai",
|
|
968
|
-
"llm.token_count.completion": 5,
|
|
969
|
-
"llm.token_count.prompt": 12,
|
|
970
|
-
"llm.token_count.total": 17,
|
|
971
|
-
"openinference.span.kind": "LLM",
|
|
972
|
-
"output.mime_type": "text/plain",
|
|
973
|
-
"output.value": "This is a test",
|
|
974
|
-
"session.id": "session-id",
|
|
975
|
-
}
|
|
976
|
-
`);
|
|
977
|
-
}));
|
|
978
|
-
});
|
|
979
|
-
describe("OpenAIInstrumentation with TraceConfig", () => {
|
|
980
|
-
const tracerProvider = new sdk_trace_node_1.NodeTracerProvider();
|
|
981
|
-
tracerProvider.register();
|
|
982
|
-
const instrumentation = new src_1.OpenAIInstrumentation({
|
|
983
|
-
traceConfig: { hideInputs: true },
|
|
984
|
-
});
|
|
985
|
-
instrumentation.disable();
|
|
986
|
-
let openai;
|
|
987
|
-
instrumentation.setTracerProvider(tracerProvider);
|
|
988
|
-
tracerProvider.addSpanProcessor(new sdk_trace_base_1.SimpleSpanProcessor(memoryExporter));
|
|
989
|
-
// @ts-expect-error the moduleExports property is private. This is needed to make the test work with auto-mocking
|
|
990
|
-
instrumentation._modules[0].moduleExports = OpenAI;
|
|
991
|
-
beforeAll(() => {
|
|
992
|
-
instrumentation.enable();
|
|
993
|
-
openai = new OpenAI.OpenAI({
|
|
994
|
-
apiKey: "fake-api-key",
|
|
995
|
-
});
|
|
996
|
-
});
|
|
997
|
-
afterAll(() => {
|
|
998
|
-
instrumentation.disable();
|
|
999
|
-
});
|
|
1000
|
-
beforeEach(() => {
|
|
1001
|
-
memoryExporter.reset();
|
|
1002
|
-
});
|
|
1003
|
-
afterEach(() => {
|
|
1004
|
-
jest.clearAllMocks();
|
|
1005
|
-
});
|
|
1006
|
-
it("is patched", () => {
|
|
1007
|
-
expect(OpenAI.openInferencePatched).toBe(true);
|
|
1008
|
-
expect((0, src_1.isPatched)()).toBe(true);
|
|
1009
|
-
});
|
|
1010
|
-
it("should respect a trace config and mask attributes accordingly", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1011
|
-
const response = {
|
|
1012
|
-
id: "cmpl-8fZu1H3VijJUWev9asnxaYyQvJTC9",
|
|
1013
|
-
object: "text_completion",
|
|
1014
|
-
created: 1704920149,
|
|
1015
|
-
model: "gpt-3.5-turbo-instruct",
|
|
1016
|
-
choices: [
|
|
1017
|
-
{
|
|
1018
|
-
text: "This is a test",
|
|
1019
|
-
index: 0,
|
|
1020
|
-
logprobs: null,
|
|
1021
|
-
finish_reason: "stop",
|
|
1022
|
-
},
|
|
1023
|
-
],
|
|
1024
|
-
usage: { prompt_tokens: 12, completion_tokens: 5, total_tokens: 17 },
|
|
1025
|
-
};
|
|
1026
|
-
// Mock out the completions endpoint
|
|
1027
|
-
jest.spyOn(openai, "post").mockImplementation(
|
|
1028
|
-
// @ts-expect-error the response type is not correct - this is just for testing
|
|
1029
|
-
() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1030
|
-
return response;
|
|
1031
|
-
}));
|
|
1032
|
-
yield openai.completions.create({
|
|
1033
|
-
prompt: "Say this is a test",
|
|
1034
|
-
model: "gpt-3.5-turbo-instruct",
|
|
1035
|
-
});
|
|
1036
|
-
const spans = memoryExporter.getFinishedSpans();
|
|
1037
|
-
expect(spans.length).toBe(1);
|
|
1038
|
-
const span = spans[0];
|
|
1039
|
-
expect(span.name).toBe("OpenAI Completions");
|
|
1040
|
-
expect(span.attributes).toMatchInlineSnapshot(`
|
|
1041
|
-
{
|
|
1042
|
-
"input.value": "__REDACTED__",
|
|
1043
|
-
"llm.invocation_parameters": "{"model":"gpt-3.5-turbo-instruct"}",
|
|
1044
|
-
"llm.model_name": "gpt-3.5-turbo-instruct",
|
|
1045
|
-
"llm.provider": "openai",
|
|
1046
|
-
"llm.system": "openai",
|
|
1047
|
-
"llm.token_count.completion": 5,
|
|
1048
|
-
"llm.token_count.prompt": 12,
|
|
1049
|
-
"llm.token_count.total": 17,
|
|
1050
|
-
"openinference.span.kind": "LLM",
|
|
1051
|
-
"output.mime_type": "text/plain",
|
|
1052
|
-
"output.value": "This is a test",
|
|
1053
|
-
}
|
|
1054
|
-
`);
|
|
1055
|
-
}));
|
|
1056
|
-
});
|
|
1057
|
-
//# sourceMappingURL=openai.test.js.map
|