feat: implement proxy input file resolution and storage

- Added `inputFiles.ts` for handling input file resolution, including validation and normalization of file types.
- Introduced `proxyFileStore.ts` for managing file storage, retrieval, and deletion with owner-based isolation.
- Created tests for proxy file storage functionality in `proxyFileStore.test.ts`.
- Developed `proxyInputFileResolver.ts` to resolve input files in OpenAI and response formats.
- Added utility functions for handling reasoning signatures in `reasoningTransport.ts` and corresponding tests.
- Enhanced shared input file transformers in `inputFile.ts` for better file handling and type inference.
This commit is contained in:
cita committed 2026-03-08 22:10:31 +08:00
1 parent ede8998244
commit 7bd3114ea0
54 files changed
+4504 -164

No files matched your search

@@ -0,0 +1,81 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
const getProxyFileByPublicIdForOwnerMock = vi.fn();
vi.mock('./proxyFileStore.js', () => ({
getProxyFileByPublicIdForOwner: (...args: unknown[]) => getProxyFileByPublicIdForOwnerMock(...args),
LOCAL_PROXY_FILE_ID_PREFIX: 'file-metapi-',
}));
describe('proxyInputFileResolver', () => {
beforeEach(() => {
getProxyFileByPublicIdForOwnerMock.mockReset();
});
it('preserves non-local file ids without resolving them from the local store', async () => {
const { resolveResponsesBodyInputFiles } = await import('./proxyInputFileResolver.js');
const body = {
model: 'gpt-5',
input: [
{
type: 'message',
role: 'user',
content: [
{
type: 'input_file',
file_id: 'file_external_123',
},
],
},
],
};
await expect(resolveResponsesBodyInputFiles(
body,
{ ownerType: 'global_proxy_token', ownerId: 'global' },
)).resolves.toEqual(body);
expect(getProxyFileByPublicIdForOwnerMock).not.toHaveBeenCalled();
});
it('resolves object-form responses input payloads with local file ids', async () => {
getProxyFileByPublicIdForOwnerMock.mockResolvedValue({
publicId: 'file-metapi-123',
filename: 'brief.pdf',
mimeType: 'application/pdf',
contentBase64: Buffer.from('%PDF-local').toString('base64'),
});
const { resolveResponsesBodyInputFiles } = await import('./proxyInputFileResolver.js');
await expect(resolveResponsesBodyInputFiles(
{
model: 'gpt-5',
input: {
type: 'message',
role: 'user',
content: [
{
type: 'input_file',
file_id: 'file-metapi-123',
},
],
},
},
{ ownerType: 'managed_key', ownerId: '9' },
)).resolves.toEqual({
model: 'gpt-5',
input: {
type: 'message',
role: 'user',
content: [
{
type: 'input_file',
file_id: 'file-metapi-123',
filename: 'brief.pdf',
file_data: Buffer.from('%PDF-local').toString('base64'),
mime_type: 'application/pdf',
},
],
},
});
});
});