@arizeai/openinference-instrumentation-openai 4.3.2 → 4.4.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/README.md +21 -0
- package/dist/esm/decisionsAttributes.d.ts +35 -0
- package/dist/esm/decisionsAttributes.d.ts.map +1 -0
- package/dist/esm/decisionsAttributes.js +55 -0
- package/dist/esm/decisionsAttributes.js.map +1 -0
- package/dist/esm/instrumentation.d.ts.map +1 -1
- package/dist/esm/instrumentation.js +98 -24
- package/dist/esm/instrumentation.js.map +1 -1
- package/dist/esm/responsesAttributes.d.ts.map +1 -1
- package/dist/esm/responsesAttributes.js +6 -3
- package/dist/esm/responsesAttributes.js.map +1 -1
- 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/esnext/decisionsAttributes.d.ts +35 -0
- package/dist/esnext/decisionsAttributes.d.ts.map +1 -0
- package/dist/esnext/decisionsAttributes.js +50 -0
- package/dist/esnext/decisionsAttributes.js.map +1 -0
- package/dist/esnext/instrumentation.d.ts.map +1 -1
- package/dist/esnext/instrumentation.js +95 -24
- package/dist/esnext/instrumentation.js.map +1 -1
- package/dist/esnext/responsesAttributes.d.ts.map +1 -1
- package/dist/esnext/responsesAttributes.js +6 -4
- package/dist/esnext/responsesAttributes.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/decisionsAttributes.d.ts +35 -0
- package/dist/src/decisionsAttributes.d.ts.map +1 -0
- package/dist/src/decisionsAttributes.js +54 -0
- package/dist/src/decisionsAttributes.js.map +1 -0
- package/dist/src/instrumentation.d.ts.map +1 -1
- package/dist/src/instrumentation.js +95 -24
- package/dist/src/instrumentation.js.map +1 -1
- package/dist/src/responsesAttributes.d.ts.map +1 -1
- package/dist/src/responsesAttributes.js +6 -4
- package/dist/src/responsesAttributes.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 +2 -2
- package/src/decisionsAttributes.ts +74 -0
- package/src/instrumentation.ts +114 -25
- package/src/responsesAttributes.ts +7 -4
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arizeai/openinference-instrumentation-openai",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OpenInference instrumentation for OpenAI",
|
|
6
6
|
"keywords": [],
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@opentelemetry/sdk-trace-node": "^1.25.1",
|
|
39
39
|
"@opentelemetry/semantic-conventions": "^1.25.1",
|
|
40
40
|
"@types/node": "^20.14.11",
|
|
41
|
-
"openai": "^
|
|
41
|
+
"openai": "^7.30.0",
|
|
42
42
|
"typescript": "^7.0.2",
|
|
43
43
|
"vitest": "^4.0.3",
|
|
44
44
|
"zod": "^3.24.3"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Attributes } from "@opentelemetry/api";
|
|
2
|
+
import type { Decision, DecisionCreateParams } from "openai/resources/decisions";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getDecisionAttributes,
|
|
6
|
+
getInputAttributes,
|
|
7
|
+
getOutputAttributes,
|
|
8
|
+
safelyJSONStringify,
|
|
9
|
+
} from "@arizeai/openinference-core";
|
|
10
|
+
import {
|
|
11
|
+
DecisionProvider,
|
|
12
|
+
DecisionSystem,
|
|
13
|
+
MimeType,
|
|
14
|
+
} from "@arizeai/openinference-semantic-conventions";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Builds the OpenInference attributes for a Decisions API request.
|
|
18
|
+
*
|
|
19
|
+
* Decision spans identify the model under `decision.*` rather than `llm.*`,
|
|
20
|
+
* and carry the raw request as JSON on `input.value` instead of input
|
|
21
|
+
* messages: the interesting part of a decision request is the set of typed
|
|
22
|
+
* questions and candidate answers, which has no message equivalent.
|
|
23
|
+
*
|
|
24
|
+
* @param body - The `decisions.create` request body.
|
|
25
|
+
* @returns `decision.system`, `decision.provider`, `decision.request.model_name`,
|
|
26
|
+
* `decision.model_name` and the input attributes.
|
|
27
|
+
* @see https://github.com/Arize-ai/openinference/blob/main/spec/decision_spans.md
|
|
28
|
+
*/
|
|
29
|
+
export function getDecisionsRequestAttributes(body: DecisionCreateParams): Attributes {
|
|
30
|
+
const input = safelyJSONStringify(body);
|
|
31
|
+
return {
|
|
32
|
+
...getDecisionAttributes({
|
|
33
|
+
system: DecisionSystem.OPENAI,
|
|
34
|
+
provider: DecisionProvider.OPENAI,
|
|
35
|
+
requestModelName: body.model,
|
|
36
|
+
}),
|
|
37
|
+
...(input != null ? getInputAttributes({ value: input, mimeType: MimeType.JSON }) : {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Builds the OpenInference attributes for a Decisions API response.
|
|
43
|
+
*
|
|
44
|
+
* Records the raw response as JSON on `output.value`, the model that served
|
|
45
|
+
* the decision as `decision.response.model_name` (and as `decision.model_name`,
|
|
46
|
+
* falling back to the requested model when the response omits it), and the
|
|
47
|
+
* reported usage as `decision.token_count.input` / `decision.token_count.output`.
|
|
48
|
+
*
|
|
49
|
+
* @param args - Attribute sources.
|
|
50
|
+
* @param args.response - The parsed `decisions.create` response.
|
|
51
|
+
* @param args.requestModelName - The model named in the request, used as the
|
|
52
|
+
* `decision.model_name` fallback.
|
|
53
|
+
* @returns The output and decision response attributes.
|
|
54
|
+
*/
|
|
55
|
+
export function getDecisionsResponseAttributes({
|
|
56
|
+
response,
|
|
57
|
+
requestModelName,
|
|
58
|
+
}: {
|
|
59
|
+
response: Decision;
|
|
60
|
+
requestModelName?: string;
|
|
61
|
+
}): Attributes {
|
|
62
|
+
const output = safelyJSONStringify(response);
|
|
63
|
+
return {
|
|
64
|
+
...(output != null ? getOutputAttributes({ value: output, mimeType: MimeType.JSON }) : {}),
|
|
65
|
+
...getDecisionAttributes({
|
|
66
|
+
requestModelName,
|
|
67
|
+
responseModelName: response.model,
|
|
68
|
+
tokenCount: {
|
|
69
|
+
input: response.usage?.input_tokens,
|
|
70
|
+
output: response.usage?.output_tokens,
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
}
|
package/src/instrumentation.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
ChatCompletionMessageParam,
|
|
22
22
|
} from "openai/resources/chat/completions";
|
|
23
23
|
import type { CompletionCreateParamsBase } from "openai/resources/completions";
|
|
24
|
+
import type { Decision } from "openai/resources/decisions";
|
|
24
25
|
import type {
|
|
25
26
|
Response as ResponseType,
|
|
26
27
|
ResponseCreateParamsBase,
|
|
@@ -38,6 +39,10 @@ import {
|
|
|
38
39
|
SemanticConventions,
|
|
39
40
|
} from "@arizeai/openinference-semantic-conventions";
|
|
40
41
|
|
|
42
|
+
import {
|
|
43
|
+
getDecisionsRequestAttributes,
|
|
44
|
+
getDecisionsResponseAttributes,
|
|
45
|
+
} from "./decisionsAttributes";
|
|
41
46
|
import {
|
|
42
47
|
consumeResponseStreamEvents,
|
|
43
48
|
getResponsesInputMessagesAttributes,
|
|
@@ -322,6 +327,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
322
327
|
},
|
|
323
328
|
});
|
|
324
329
|
const execContext = getExecContext(span);
|
|
330
|
+
const recordError = (error: unknown) => endSpanWithError(span, error);
|
|
325
331
|
const execPromise = safeExecuteInTheMiddle(
|
|
326
332
|
() => {
|
|
327
333
|
return context.with(trace.setSpan(execContext, span), () => {
|
|
@@ -331,12 +337,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
331
337
|
(error) => {
|
|
332
338
|
// Push the error to the span
|
|
333
339
|
if (error) {
|
|
334
|
-
|
|
335
|
-
span.setStatus({
|
|
336
|
-
code: SpanStatusCode.ERROR,
|
|
337
|
-
message: error.message,
|
|
338
|
-
});
|
|
339
|
-
span.end();
|
|
340
|
+
recordError(error);
|
|
340
341
|
}
|
|
341
342
|
},
|
|
342
343
|
);
|
|
@@ -370,6 +371,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
370
371
|
|
|
371
372
|
return result;
|
|
372
373
|
};
|
|
374
|
+
observeRejection(execPromise, recordError);
|
|
373
375
|
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
374
376
|
return context.bind(execContext, wrappedPromise);
|
|
375
377
|
};
|
|
@@ -399,7 +401,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
399
401
|
},
|
|
400
402
|
});
|
|
401
403
|
const execContext = getExecContext(span);
|
|
402
|
-
|
|
404
|
+
const recordError = (error: unknown) => endSpanWithError(span, error);
|
|
403
405
|
const execPromise = safeExecuteInTheMiddle(
|
|
404
406
|
() => {
|
|
405
407
|
return context.with(trace.setSpan(execContext, span), () => {
|
|
@@ -409,12 +411,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
409
411
|
(error) => {
|
|
410
412
|
// Push the error to the span
|
|
411
413
|
if (error) {
|
|
412
|
-
|
|
413
|
-
span.setStatus({
|
|
414
|
-
code: SpanStatusCode.ERROR,
|
|
415
|
-
message: error.message,
|
|
416
|
-
});
|
|
417
|
-
span.end();
|
|
414
|
+
recordError(error);
|
|
418
415
|
}
|
|
419
416
|
},
|
|
420
417
|
);
|
|
@@ -434,6 +431,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
434
431
|
}
|
|
435
432
|
return result;
|
|
436
433
|
};
|
|
434
|
+
observeRejection(execPromise, recordError);
|
|
437
435
|
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
438
436
|
return context.bind(execContext, wrappedPromise);
|
|
439
437
|
};
|
|
@@ -467,6 +465,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
467
465
|
},
|
|
468
466
|
});
|
|
469
467
|
const execContext = getExecContext(span);
|
|
468
|
+
const recordError = (error: unknown) => endSpanWithError(span, error);
|
|
470
469
|
const execPromise = safeExecuteInTheMiddle(
|
|
471
470
|
() => {
|
|
472
471
|
return context.with(trace.setSpan(execContext, span), () => {
|
|
@@ -476,12 +475,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
476
475
|
(error) => {
|
|
477
476
|
// Push the error to the span
|
|
478
477
|
if (error) {
|
|
479
|
-
|
|
480
|
-
span.setStatus({
|
|
481
|
-
code: SpanStatusCode.ERROR,
|
|
482
|
-
message: error.message,
|
|
483
|
-
});
|
|
484
|
-
span.end();
|
|
478
|
+
recordError(error);
|
|
485
479
|
}
|
|
486
480
|
},
|
|
487
481
|
);
|
|
@@ -497,6 +491,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
497
491
|
span.end();
|
|
498
492
|
return result;
|
|
499
493
|
};
|
|
494
|
+
observeRejection(execPromise, recordError);
|
|
500
495
|
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
501
496
|
return context.bind(execContext, wrappedPromise);
|
|
502
497
|
};
|
|
@@ -531,6 +526,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
531
526
|
},
|
|
532
527
|
});
|
|
533
528
|
const execContext = getExecContext(span);
|
|
529
|
+
const recordError = (error: unknown) => endSpanWithError(span, error);
|
|
534
530
|
const execPromise = safeExecuteInTheMiddle(
|
|
535
531
|
() => {
|
|
536
532
|
return context.with(trace.setSpan(execContext, span), () => {
|
|
@@ -540,12 +536,7 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
540
536
|
(error) => {
|
|
541
537
|
// Push the error to the span
|
|
542
538
|
if (error) {
|
|
543
|
-
|
|
544
|
-
span.setStatus({
|
|
545
|
-
code: SpanStatusCode.ERROR,
|
|
546
|
-
message: error.message,
|
|
547
|
-
});
|
|
548
|
-
span.end();
|
|
539
|
+
recordError(error);
|
|
549
540
|
}
|
|
550
541
|
},
|
|
551
542
|
);
|
|
@@ -583,6 +574,58 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
583
574
|
|
|
584
575
|
return result;
|
|
585
576
|
};
|
|
577
|
+
observeRejection(execPromise, recordError);
|
|
578
|
+
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
579
|
+
return context.bind(execContext, wrappedPromise);
|
|
580
|
+
};
|
|
581
|
+
},
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Patch decisions (if the patched module contains the Decisions interface, openai >= 7.30)
|
|
586
|
+
if (module.OpenAI.Decisions) {
|
|
587
|
+
type DecisionsCreateType = typeof module.OpenAI.Decisions.prototype.create;
|
|
588
|
+
|
|
589
|
+
this._wrap(
|
|
590
|
+
module.OpenAI.Decisions.prototype,
|
|
591
|
+
"create",
|
|
592
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
593
|
+
(original: DecisionsCreateType): any => {
|
|
594
|
+
return function patchedCreate(this: unknown, ...args: Parameters<DecisionsCreateType>) {
|
|
595
|
+
const body = args[0];
|
|
596
|
+
// A decision model scores caller-supplied answers rather than generating
|
|
597
|
+
// text, so this is a DECISION span with no llm.* attributes.
|
|
598
|
+
const span = instrumentation.oiTracer.startSpan(`OpenAI Decisions`, {
|
|
599
|
+
kind: SpanKind.INTERNAL,
|
|
600
|
+
attributes: {
|
|
601
|
+
[SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.DECISION,
|
|
602
|
+
...getDecisionsRequestAttributes(body),
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
const execContext = getExecContext(span);
|
|
606
|
+
const recordError = (error: unknown) => endSpanWithError(span, error);
|
|
607
|
+
const execPromise = safeExecuteInTheMiddle(
|
|
608
|
+
() => {
|
|
609
|
+
return context.with(trace.setSpan(execContext, span), () => {
|
|
610
|
+
return original.apply(this, args);
|
|
611
|
+
});
|
|
612
|
+
},
|
|
613
|
+
(error) => {
|
|
614
|
+
// Push the error to the span
|
|
615
|
+
if (error) {
|
|
616
|
+
recordError(error);
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
);
|
|
620
|
+
const wrappedPromiseThen = (result: Decision) => {
|
|
621
|
+
span.setAttributes(
|
|
622
|
+
getDecisionsResponseAttributes({ response: result, requestModelName: body.model }),
|
|
623
|
+
);
|
|
624
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
625
|
+
span.end();
|
|
626
|
+
return result;
|
|
627
|
+
};
|
|
628
|
+
observeRejection(execPromise, recordError);
|
|
586
629
|
const wrappedPromise = invokeMaybeAPIPromise(execPromise, wrappedPromiseThen);
|
|
587
630
|
return context.bind(execContext, wrappedPromise);
|
|
588
631
|
};
|
|
@@ -613,6 +656,12 @@ export class OpenAIInstrumentation extends InstrumentationBase<typeof openai> {
|
|
|
613
656
|
this._unwrap(moduleExports.OpenAI.Chat.Completions.prototype, "create");
|
|
614
657
|
this._unwrap(moduleExports.OpenAI.Completions.prototype, "create");
|
|
615
658
|
this._unwrap(moduleExports.OpenAI.Embeddings.prototype, "create");
|
|
659
|
+
if (moduleExports.OpenAI.Responses) {
|
|
660
|
+
this._unwrap(moduleExports.OpenAI.Responses.prototype, "create");
|
|
661
|
+
}
|
|
662
|
+
if (moduleExports.OpenAI.Decisions) {
|
|
663
|
+
this._unwrap(moduleExports.OpenAI.Decisions.prototype, "create");
|
|
664
|
+
}
|
|
616
665
|
|
|
617
666
|
// Keyed the same way patch() keys it, so a re-patch is possible after.
|
|
618
667
|
_patchedModules.delete(moduleExports.OpenAI);
|
|
@@ -1037,6 +1086,46 @@ function isAPIPromise<T>(promise: unknown): promise is APIPromise<T> {
|
|
|
1037
1086
|
return promise instanceof APIPromise;
|
|
1038
1087
|
}
|
|
1039
1088
|
|
|
1089
|
+
/**
|
|
1090
|
+
* Calls `onRejected` if the SDK call rejects, so the span can be ended with an
|
|
1091
|
+
* error status.
|
|
1092
|
+
*
|
|
1093
|
+
* {@link invokeMaybeAPIPromise} only sees fulfilled values, so without this a
|
|
1094
|
+
* failed request would leave its span open. For an APIPromise the rejection is
|
|
1095
|
+
* observed through `asResponse()`, which settles with the underlying HTTP
|
|
1096
|
+
* promise and never parses the body: parsing it here as well would consume the
|
|
1097
|
+
* body before the caller reads it. So only a failed request is observed, not a
|
|
1098
|
+
* response body that fails to parse.
|
|
1099
|
+
*
|
|
1100
|
+
* @param promise - The value returned by the original SDK method
|
|
1101
|
+
* @param onRejected - Called with the rejection reason
|
|
1102
|
+
*/
|
|
1103
|
+
function observeRejection(promise: unknown, onRejected: (error: unknown) => void): void {
|
|
1104
|
+
if (isAPIPromise<unknown>(promise)) {
|
|
1105
|
+
void promise.asResponse().then(undefined, onRejected);
|
|
1106
|
+
} else if (promise instanceof Promise) {
|
|
1107
|
+
void promise.then(undefined, onRejected);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Records the error on the span, sets its status to ERROR, and ends it.
|
|
1113
|
+
*
|
|
1114
|
+
* Used both when the SDK method throws synchronously and, through
|
|
1115
|
+
* {@link observeRejection}, when its promise rejects (#3845).
|
|
1116
|
+
*
|
|
1117
|
+
* @param span - The span to end
|
|
1118
|
+
* @param error - The thrown or rejected value, which need not be an Error
|
|
1119
|
+
*/
|
|
1120
|
+
function endSpanWithError(span: Span, error: unknown): void {
|
|
1121
|
+
span.recordException(error instanceof Error ? error : String(error));
|
|
1122
|
+
span.setStatus({
|
|
1123
|
+
code: SpanStatusCode.ERROR,
|
|
1124
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1125
|
+
});
|
|
1126
|
+
span.end();
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1040
1129
|
/**
|
|
1041
1130
|
* Invokes the thennable of a promise or an APIPromise.
|
|
1042
1131
|
*
|
|
@@ -20,7 +20,7 @@ import { getNumberProperty } from "./typeUtils";
|
|
|
20
20
|
* @returns The attributes for the item
|
|
21
21
|
*/
|
|
22
22
|
function getResponseItemAttributes(
|
|
23
|
-
item: Exclude<ResponseInputItem | ResponseOutputItem, { role: string }>,
|
|
23
|
+
item: Exclude<ResponseInputItem | ResponseOutputItem, { role: string; content: unknown }>,
|
|
24
24
|
prefix = "",
|
|
25
25
|
): Attributes {
|
|
26
26
|
const attributes: Attributes = {};
|
|
@@ -40,13 +40,14 @@ function getResponseItemAttributes(
|
|
|
40
40
|
}
|
|
41
41
|
case "function_call_output": {
|
|
42
42
|
attributes[`${prefix}${SemanticConventions.MESSAGE_ROLE}`] = "tool";
|
|
43
|
-
attributes[`${prefix}${SemanticConventions.MESSAGE_TOOL_CALL_ID}`] =
|
|
43
|
+
attributes[`${prefix}${SemanticConventions.MESSAGE_TOOL_CALL_ID}`] =
|
|
44
|
+
item.call_id ?? undefined;
|
|
44
45
|
if (typeof item.output === "string") {
|
|
45
46
|
attributes[`${prefix}${SemanticConventions.MESSAGE_CONTENT}`] = item.output;
|
|
46
47
|
} else {
|
|
47
48
|
// TODO(2410): figure out how to serialize the list of tools
|
|
48
49
|
attributes[`${prefix}${SemanticConventions.MESSAGE_CONTENT}`] =
|
|
49
|
-
safelyJSONStringify(item.output)
|
|
50
|
+
safelyJSONStringify(item.output) ?? undefined;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
break;
|
|
@@ -131,7 +132,9 @@ function getResponseItemMessageAttributes(
|
|
|
131
132
|
typeof itemMessage === "string"
|
|
132
133
|
? ({ content: itemMessage, role: "user" } satisfies ResponseInputItem)
|
|
133
134
|
: itemMessage;
|
|
134
|
-
|
|
135
|
+
// Items such as `additional_tools` carry a role but no content, and are
|
|
136
|
+
// handled with the other non-message items.
|
|
137
|
+
if (!("role" in message) || !("content" in message)) {
|
|
135
138
|
return getResponseItemAttributes(message, prefix);
|
|
136
139
|
}
|
|
137
140
|
const role = message.role;
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// this is an auto-generated file. see js/scripts/version-update.js
|
|
2
|
-
export const VERSION = "4.
|
|
2
|
+
export const VERSION = "4.4.1";
|