Skip to content

Commit a2893e2

Browse files
committed
fix: persist container_id in codeInterpreterTool history (fixes #253)
1 parent fba44d9 commit a2893e2

File tree

5 files changed

+82
-10
lines changed

5 files changed

+82
-10
lines changed

.changeset/thin-kids-wink.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@openai/agents-openai': patch
3+
---
4+
5+
Persist container_id in codeInterpreterTool history (fixes #253)

packages/agents-openai/src/openaiResponsesModel.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ import {
3333
ImageGenerationStatus,
3434
WebSearchStatus,
3535
} from './tools';
36-
import { camelOrSnakeToSnakeCase } from './utils/providerData';
36+
import {
37+
camelOrSnakeToSnakeCase,
38+
snakeToCamelCase,
39+
} from './utils/providerData';
3740
import { ProviderData } from '@openai/agents-core/types';
3841

3942
type ToolChoice = ToolChoiceOptions | ToolChoiceTypes | ToolChoiceFunction;
@@ -710,7 +713,7 @@ function convertToOutputItem(
710713
name: item.type,
711714
status,
712715
output: outputData,
713-
providerData: remainingItem,
716+
providerData: snakeToCamelCase(remainingItem),
714717
};
715718
return output;
716719
} else if (item.type === 'function_call') {
@@ -722,7 +725,7 @@ function convertToOutputItem(
722725
name,
723726
status,
724727
arguments: args,
725-
providerData,
728+
providerData: snakeToCamelCase(providerData),
726729
};
727730
return output;
728731
} else if (item.type === 'computer_call') {
@@ -733,7 +736,7 @@ function convertToOutputItem(
733736
callId: call_id,
734737
status,
735738
action,
736-
providerData,
739+
providerData: snakeToCamelCase(providerData),
737740
};
738741
return output;
739742
} else if (item.type === 'mcp_list_tools') {
@@ -744,7 +747,7 @@ function convertToOutputItem(
744747
name: item.type,
745748
status: 'completed',
746749
output: undefined,
747-
providerData,
750+
providerData: snakeToCamelCase(providerData),
748751
};
749752
return output;
750753
} else if (item.type === 'mcp_approval_request') {
@@ -755,7 +758,7 @@ function convertToOutputItem(
755758
name: 'mcp_approval_request',
756759
status: 'completed',
757760
output: undefined,
758-
providerData,
761+
providerData: snakeToCamelCase(providerData),
759762
};
760763
return output;
761764
} else if (item.type === 'mcp_call') {
@@ -767,7 +770,7 @@ function convertToOutputItem(
767770
name: item.type,
768771
status: 'completed',
769772
output: outputData || undefined,
770-
providerData,
773+
providerData: snakeToCamelCase(providerData),
771774
};
772775
return output;
773776
} else if (item.type === 'reasoning') {
@@ -782,10 +785,10 @@ function convertToOutputItem(
782785
return {
783786
type: 'input_text',
784787
text,
785-
providerData: remainingContent,
788+
providerData: snakeToCamelCase(remainingContent),
786789
};
787790
}),
788-
providerData,
791+
providerData: snakeToCamelCase(providerData),
789792
};
790793
return output;
791794
}

packages/agents-openai/src/utils/providerData.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,25 @@ export function camelOrSnakeToSnakeCase<
1919
}
2020
return result;
2121
}
22+
23+
/**
24+
* Converts snake_case keys of an object to camelCase recursively.
25+
*/
26+
export function snakeToCamelCase<T extends Record<string, any> | undefined>(
27+
providerData: T | undefined,
28+
): Record<string, any> | undefined {
29+
if (
30+
!providerData ||
31+
typeof providerData !== 'object' ||
32+
Array.isArray(providerData)
33+
) {
34+
return providerData;
35+
}
36+
37+
const result: Record<string, any> = {};
38+
for (const [key, value] of Object.entries(providerData)) {
39+
const camelKey = key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
40+
result[camelKey] = snakeToCamelCase(value);
41+
}
42+
return result;
43+
}

packages/agents-openai/test/openaiResponsesModel.helpers.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,4 +328,21 @@ describe('convertToOutputItem', () => {
328328
] as any),
329329
).toThrow();
330330
});
331+
332+
it('converts snake_case provider data to camelCase', () => {
333+
const out = convertToOutputItem([
334+
{
335+
type: 'code_interpreter_call',
336+
id: 'ci',
337+
status: 'completed',
338+
container_id: 'xyz',
339+
code: 'print()',
340+
outputs: [],
341+
},
342+
] as any);
343+
expect(out[0]).toMatchObject({
344+
type: 'hosted_tool_call',
345+
providerData: { containerId: 'xyz' },
346+
});
347+
});
331348
});

packages/agents-openai/test/utils/providerData.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { describe, it, expect } from 'vitest';
2-
import { camelOrSnakeToSnakeCase } from '../../src/utils/providerData';
2+
import {
3+
camelOrSnakeToSnakeCase,
4+
snakeToCamelCase,
5+
} from '../../src/utils/providerData';
36

47
describe('camelToSnakeCase', () => {
58
it('converts flat camelCase keys to snake_case', () => {
@@ -68,3 +71,25 @@ describe('camelToSnakeCase', () => {
6871
});
6972
});
7073
});
74+
75+
describe('snakeToCamelCase', () => {
76+
it('converts snake_case keys to camelCase', () => {
77+
expect(snakeToCamelCase({ foo_bar: 1, baz_qux: 2 })).toEqual({
78+
fooBar: 1,
79+
bazQux: 2,
80+
});
81+
});
82+
83+
it('handles nested objects', () => {
84+
expect(
85+
snakeToCamelCase({
86+
outer_key: { inner_key: 42, another: { deep_key: 'x' } },
87+
}),
88+
).toEqual({ outerKey: { innerKey: 42, another: { deepKey: 'x' } } });
89+
});
90+
91+
it('leaves arrays and primitives unchanged', () => {
92+
expect(snakeToCamelCase([1, 2, 3])).toEqual([1, 2, 3]);
93+
expect(snakeToCamelCase(undefined)).toBe(undefined);
94+
});
95+
});

0 commit comments

Comments
 (0)