forked from boos/BoosAPI
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:
@@ -523,6 +523,8 @@ describe('Gemini aggregate/outbound/usage', () => {
|
||||
completionTokens: 11,
|
||||
totalTokens: 31,
|
||||
cachedTokens: 4,
|
||||
cacheReadTokens: 4,
|
||||
cacheCreationTokens: 0,
|
||||
reasoningTokens: 4,
|
||||
});
|
||||
expect(extractGeminiUsage(serialized)).toEqual({
|
||||
@@ -530,6 +532,8 @@ describe('Gemini aggregate/outbound/usage', () => {
|
||||
completionTokens: 11,
|
||||
totalTokens: 31,
|
||||
cachedTokens: 4,
|
||||
cacheReadTokens: 4,
|
||||
cacheCreationTokens: 0,
|
||||
reasoningTokens: 4,
|
||||
});
|
||||
});
|
||||
@@ -550,6 +554,8 @@ describe('Gemini aggregate/outbound/usage', () => {
|
||||
completionTokens: 6,
|
||||
totalTokens: 6,
|
||||
cachedTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheCreationTokens: 0,
|
||||
reasoningTokens: 0,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -570,6 +570,8 @@ describe('extractGeminiUsage', () => {
|
||||
completionTokens: 10,
|
||||
totalTokens: 18,
|
||||
cachedTokens: 5,
|
||||
cacheReadTokens: 5,
|
||||
cacheCreationTokens: 0,
|
||||
reasoningTokens: 3,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,10 @@ function baseIncludesVersion(baseUrl: string): boolean {
|
||||
return /\/v\d+(?:beta)?(?:\/|$)/i.test(baseUrl);
|
||||
}
|
||||
|
||||
function asTrimmedString(value: unknown): string {
|
||||
return typeof value === 'string' ? value.trim() : '';
|
||||
}
|
||||
|
||||
export function resolveGeminiNativeBaseUrl(baseUrl: string, apiVersion: string): string {
|
||||
const normalized = normalizeBaseUrl(baseUrl);
|
||||
if (baseIncludesVersion(normalized)) return normalized;
|
||||
@@ -37,6 +41,42 @@ export function resolveGeminiGenerateContentUrl(
|
||||
return `${base}/${normalizedAction}${query ? `?${query}` : ''}`;
|
||||
}
|
||||
|
||||
export function resolveGeminiProxyApiVersion(params: { geminiApiVersion?: unknown } | null | undefined): string {
|
||||
return asTrimmedString(params?.geminiApiVersion) || 'v1beta';
|
||||
}
|
||||
|
||||
export function parseGeminiProxyRequestPath(input: {
|
||||
rawUrl?: string | null;
|
||||
params?: { geminiApiVersion?: unknown } | null;
|
||||
}): {
|
||||
apiVersion: string;
|
||||
modelActionPath: string;
|
||||
requestedModel: string;
|
||||
isStreamAction: boolean;
|
||||
} {
|
||||
const apiVersion = resolveGeminiProxyApiVersion(input.params);
|
||||
const rawUrl = asTrimmedString(input.rawUrl);
|
||||
const withoutQuery = rawUrl.split('?')[0] || rawUrl;
|
||||
const normalizedVersion = apiVersion.replace(/^\/+/, '');
|
||||
const geminiPrefix = `/gemini/${normalizedVersion}/`;
|
||||
const aliasPrefix = `/${normalizedVersion}/`;
|
||||
|
||||
let modelActionPath = withoutQuery.replace(/^\/+/, '');
|
||||
if (withoutQuery.startsWith(geminiPrefix)) {
|
||||
modelActionPath = withoutQuery.slice(geminiPrefix.length);
|
||||
} else if (withoutQuery.startsWith(aliasPrefix)) {
|
||||
modelActionPath = withoutQuery.slice(aliasPrefix.length);
|
||||
}
|
||||
|
||||
const requestedModel = modelActionPath.replace(/^models\//, '').split(':')[0].trim();
|
||||
return {
|
||||
apiVersion,
|
||||
modelActionPath,
|
||||
requestedModel,
|
||||
isStreamAction: modelActionPath.endsWith(':streamGenerateContent'),
|
||||
};
|
||||
}
|
||||
|
||||
import { geminiGenerateContentInbound } from './inbound.js';
|
||||
import { geminiGenerateContentOutbound } from './outbound.js';
|
||||
import { geminiGenerateContentStream } from './stream.js';
|
||||
@@ -58,6 +98,8 @@ export const geminiGenerateContentTransformer = {
|
||||
reasoningEffortToGeminiThinkingConfig,
|
||||
geminiThinkingConfigToReasoning,
|
||||
},
|
||||
parseProxyRequestPath: parseGeminiProxyRequestPath,
|
||||
resolveProxyApiVersion: resolveGeminiProxyApiVersion,
|
||||
resolveBaseUrl: resolveGeminiNativeBaseUrl,
|
||||
resolveModelsUrl: resolveGeminiModelsUrl,
|
||||
resolveActionUrl: resolveGeminiGenerateContentUrl,
|
||||
|
||||
@@ -37,6 +37,8 @@ export function extractGeminiUsage(payload: unknown): NormalizedUsage {
|
||||
completionTokens,
|
||||
totalTokens,
|
||||
cachedTokens,
|
||||
cacheReadTokens: cachedTokens,
|
||||
cacheCreationTokens: 0,
|
||||
reasoningTokens,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user