Skip to content

Commit 995f0b4

Browse files
committed
Ensure pure strings are not stringified
1 parent 03ad550 commit 995f0b4

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

packages/core/src/tracing/ai/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ export function endStreamSpan(span: Span, state: StreamResponseState, recordOutp
169169
span.end();
170170
}
171171

172+
/**
173+
* Serialize a value to a JSON string without truncation.
174+
* Strings are returned as-is, arrays and objects are JSON-stringified.
175+
*/
176+
export function getJsonString<T>(value: T | T[]): string {
177+
if (typeof value === 'string') {
178+
return value;
179+
}
180+
return JSON.stringify(value);
181+
}
182+
172183
/**
173184
* Get the truncated JSON string for a string or array of strings.
174185
*

packages/core/src/tracing/openai/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { InstrumentedMethodEntry } from '../ai/utils';
1919
import {
2020
buildMethodPath,
2121
extractSystemInstructions,
22+
getJsonString,
2223
getTruncatedJsonString,
2324
resolveAIRecordingOptions,
2425
wrapPromiseWithMethods,
@@ -126,7 +127,7 @@ function addRequestAttributes(
126127

127128
span.setAttribute(
128129
GEN_AI_INPUT_MESSAGES_ATTRIBUTE,
129-
enableTruncation ? getTruncatedJsonString(filteredMessages) : JSON.stringify(filteredMessages),
130+
enableTruncation ? getTruncatedJsonString(filteredMessages) : getJsonString(filteredMessages),
130131
);
131132

132133
if (Array.isArray(filteredMessages)) {

packages/core/test/tracing/openai-enable-truncation.test.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ function createMockOpenAiClient() {
1919
}),
2020
},
2121
},
22+
responses: {
23+
create: async (params: { model: string; input: string }) => ({
24+
id: 'resp-test',
25+
object: 'response',
26+
model: params.model,
27+
output_text: 'Response text',
28+
status: 'completed',
29+
usage: { input_tokens: 5, output_tokens: 3, total_tokens: 8 },
30+
}),
31+
},
2232
};
2333
}
2434

@@ -44,7 +54,7 @@ describe('OpenAI enableTruncation option', () => {
4454

4555
async function callWithOptions(
4656
options: { recordInputs?: boolean; enableTruncation?: boolean },
47-
messages: Array<{ role: string; content: string }>,
57+
input: Array<{ role: string; content: string }> | string,
4858
): Promise<string | undefined> {
4959
const mockClient = createMockOpenAiClient();
5060
const instrumented = instrumentOpenAiClient(mockClient as unknown as OpenAiClient, options) as MockClient;
@@ -53,7 +63,11 @@ describe('OpenAI enableTruncation option', () => {
5363

5464
await startSpan({ name: 'test' }, async span => {
5565
rootSpan = span;
56-
await instrumented.chat.completions.create({ model: 'gpt-4', messages });
66+
if (typeof input === 'string') {
67+
await instrumented.responses.create({ model: 'gpt-4', input });
68+
} else {
69+
await instrumented.chat.completions.create({ model: 'gpt-4', messages: input });
70+
}
5771
});
5872

5973
const spans = getSpanDescendants(rootSpan!);
@@ -83,4 +97,10 @@ describe('OpenAI enableTruncation option', () => {
8397
const parsed = JSON.parse(inputMessages!);
8498
expect(parsed).toEqual(messages);
8599
});
100+
101+
it('does not wrap string input in quotes when enableTruncation is false', async () => {
102+
const stringInput = 'Translate this to French: Hello';
103+
const inputMessages = await callWithOptions({ recordInputs: true, enableTruncation: false }, stringInput);
104+
expect(inputMessages).toBe(stringInput);
105+
});
86106
});

0 commit comments

Comments
 (0)