refactor: 消除剩余重复代码,统一 usage 处理和导入风格
- 提取 checkoutResponsesUsageData 到 shared,消除两个 responses proxy 重复 - claude-to-openai 复用 usage-utils 的 normalizeUsage(支持 cached_tokens) - 删除 utils.ts 默认导出,统一使用命名导入 - channel-resolver JSON.parse 加防御,坏数据跳过不崩溃 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
07a597e41a
commit
f7a1d95ec4
8 files changed
+48
-80
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { Context } from "hono";
|
||||
|
||||
import { CONSTANTS } from "../constants";
|
||||
import utils from "../utils";
|
||||
import { getJsonSetting } from "../utils";
|
||||
|
||||
// Token 工具对象
|
||||
export const TokenUtils = {
|
||||
@@ -24,7 +24,7 @@ export const TokenUtils = {
|
||||
}
|
||||
|
||||
// Fallback to global pricing
|
||||
const globalPricingMap = await utils.getJsonSetting(c, CONSTANTS.MODEL_PRICING_KEY);
|
||||
const globalPricingMap = await getJsonSetting(c, CONSTANTS.MODEL_PRICING_KEY);
|
||||
return globalPricingMap?.[model] || null;
|
||||
},
|
||||
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
import { Context } from "hono";
|
||||
import { CONSTANTS } from "../constants";
|
||||
import utils from "../utils";
|
||||
import { getSetting, saveSetting } from "../utils";
|
||||
|
||||
const DB_INIT_QUERIES = `
|
||||
CREATE TABLE IF NOT EXISTS channel_config (
|
||||
@@ -34,23 +34,23 @@ const dbOperations = {
|
||||
.join(";\n");
|
||||
await c.env.DB.exec(query);
|
||||
|
||||
const version = await utils.getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||
const version = await getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||
if (version) {
|
||||
return c.json({ message: "Database already initialized" });
|
||||
}
|
||||
await utils.saveSetting(c, CONSTANTS.DB_VERSION_KEY, CONSTANTS.DB_VERSION);
|
||||
await saveSetting(c, CONSTANTS.DB_VERSION_KEY, CONSTANTS.DB_VERSION);
|
||||
},
|
||||
migrate: async (c: Context<HonoCustomType>) => {
|
||||
const version = await utils.getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||
const version = await getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||
if (version != CONSTANTS.DB_VERSION) {
|
||||
// TODO: Perform migration logic here
|
||||
|
||||
// Update the version in the settings table
|
||||
await utils.saveSetting(c, CONSTANTS.DB_VERSION_KEY, CONSTANTS.DB_VERSION);
|
||||
await saveSetting(c, CONSTANTS.DB_VERSION_KEY, CONSTANTS.DB_VERSION);
|
||||
}
|
||||
},
|
||||
getVersion: async (c: Context<HonoCustomType>): Promise<string | null> => {
|
||||
return await utils.getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||
return await getSetting(c, CONSTANTS.DB_VERSION_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Context } from "hono"
|
||||
import {
|
||||
handleStreamResponse,
|
||||
OpenAIResponsesResponse,
|
||||
extractUsageFromResponse,
|
||||
estimateUsageFromBodies,
|
||||
checkoutResponsesUsageData,
|
||||
} from "./shared/responses-stream-utils"
|
||||
|
||||
const buildProxyRequest = (
|
||||
@@ -58,20 +56,7 @@ export default {
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
try {
|
||||
const resJson = await response.clone().json<OpenAIResponsesResponse>()
|
||||
const usage = extractUsageFromResponse(resJson)
|
||||
if (usage) {
|
||||
await saveUsage(usage)
|
||||
} else {
|
||||
const estimatedUsage = estimateUsageFromBodies(requestBody, resJson)
|
||||
if (estimatedUsage) {
|
||||
await saveUsage(estimatedUsage)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing response JSON for usage:", error)
|
||||
}
|
||||
await checkoutResponsesUsageData(saveUsage, response, requestBody)
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import { Context } from "hono"
|
||||
|
||||
type OpenAIUsageLike = {
|
||||
prompt_tokens?: number;
|
||||
completion_tokens?: number;
|
||||
total_tokens?: number;
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
}
|
||||
import { normalizeUsage as normalizeResponsesUsage } from "./shared/usage-utils"
|
||||
|
||||
type OpenAIStreamChoice = {
|
||||
index?: number;
|
||||
@@ -30,7 +23,13 @@ type OpenAIStreamChunk = {
|
||||
id?: string;
|
||||
model?: string;
|
||||
choices?: OpenAIStreamChoice[];
|
||||
usage?: OpenAIUsageLike;
|
||||
usage?: {
|
||||
prompt_tokens?: number;
|
||||
completion_tokens?: number;
|
||||
total_tokens?: number;
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
};
|
||||
}
|
||||
|
||||
const mapFinishReasonToClaude = (finishReason?: string | null): string | null => {
|
||||
@@ -261,23 +260,6 @@ const convertOpenAIResponseToClaude = (resJson: any): any => {
|
||||
};
|
||||
}
|
||||
|
||||
const normalizeUsage = (raw?: OpenAIUsageLike): Usage | null => {
|
||||
if (!raw) return null;
|
||||
const promptTokens = raw.prompt_tokens ?? raw.input_tokens;
|
||||
const completionTokens = raw.completion_tokens ?? raw.output_tokens;
|
||||
const totalTokens = raw.total_tokens ?? (
|
||||
(promptTokens ?? 0) + (completionTokens ?? 0)
|
||||
);
|
||||
if (promptTokens == null && completionTokens == null && totalTokens == null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
prompt_tokens: promptTokens,
|
||||
completion_tokens: completionTokens,
|
||||
total_tokens: totalTokens,
|
||||
};
|
||||
}
|
||||
|
||||
const createClaudeStreamTransformer = (
|
||||
saveUsage: (usage: Usage) => Promise<void>
|
||||
): TransformStream<Uint8Array, Uint8Array> => {
|
||||
@@ -441,7 +423,7 @@ const createClaudeStreamTransformer = (
|
||||
if (!chunkJson) return;
|
||||
|
||||
if (chunkJson.usage) {
|
||||
const normalized = normalizeUsage(chunkJson.usage);
|
||||
const normalized = normalizeResponsesUsage(chunkJson.usage);
|
||||
if (normalized) {
|
||||
usageToSave = normalized;
|
||||
outputTokens = normalized.completion_tokens ?? outputTokens;
|
||||
@@ -583,7 +565,7 @@ export default {
|
||||
if (response.ok) {
|
||||
try {
|
||||
const resJson = await response.clone().json<OpenAIResponse & Record<string, any>>();
|
||||
const usage = normalizeUsage(resJson?.usage);
|
||||
const usage = normalizeResponsesUsage(resJson?.usage);
|
||||
if (usage) {
|
||||
await saveUsage(usage);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Context } from "hono"
|
||||
import {
|
||||
handleStreamResponse,
|
||||
OpenAIResponsesResponse,
|
||||
extractUsageFromResponse,
|
||||
estimateUsageFromBodies,
|
||||
checkoutResponsesUsageData,
|
||||
} from "./shared/responses-stream-utils"
|
||||
|
||||
const buildProxyRequest = (
|
||||
@@ -51,20 +49,7 @@ export default {
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
try {
|
||||
const resJson = await response.clone().json<OpenAIResponsesResponse>()
|
||||
const usage = extractUsageFromResponse(resJson)
|
||||
if (usage) {
|
||||
await saveUsage(usage)
|
||||
} else {
|
||||
const estimatedUsage = estimateUsageFromBodies(requestBody, resJson)
|
||||
if (estimatedUsage) {
|
||||
await saveUsage(estimatedUsage)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing response JSON for usage:", error)
|
||||
}
|
||||
await checkoutResponsesUsageData(saveUsage, response, requestBody)
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
@@ -53,7 +53,11 @@ export const resolveChannel = async (
|
||||
const availableChannels: Array<{ key: string, config: ChannelConfig, mapping: { pattern: string, deployment: string } }> = [];
|
||||
|
||||
for (const row of channelsResult.results) {
|
||||
const config = JSON.parse(row.value) as ChannelConfig;
|
||||
const config = (() => { try { return JSON.parse(row.value) as ChannelConfig; } catch { return null; } })();
|
||||
if (!config) {
|
||||
console.error(`Invalid channel config for key: ${row.key}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allowedTypes && (!config.type || !allowedTypes.includes(config.type))) {
|
||||
continue;
|
||||
|
||||
@@ -95,4 +95,25 @@ export const handleStreamResponse = async (
|
||||
}
|
||||
}
|
||||
|
||||
export const checkoutResponsesUsageData = async (
|
||||
saveUsage: (usage: Usage) => Promise<void>,
|
||||
response: Response,
|
||||
requestBody: any,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const resJson = await response.clone().json<OpenAIResponsesResponse>()
|
||||
const usage = extractUsageFromResponse(resJson)
|
||||
if (usage) {
|
||||
await saveUsage(usage)
|
||||
} else {
|
||||
const estimatedUsage = estimateUsageFromBodies(requestBody, resJson)
|
||||
if (estimatedUsage) {
|
||||
await saveUsage(estimatedUsage)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing response JSON for usage:", error)
|
||||
}
|
||||
}
|
||||
|
||||
export { OpenAIResponsesResponse, extractUsageFromResponse, estimateUsageFromBodies }
|
||||
@@ -106,12 +106,3 @@ export const findDeploymentMapping = (
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default {
|
||||
getJsonObjectValue,
|
||||
getJsonSetting,
|
||||
getSetting,
|
||||
saveSetting,
|
||||
wildcardMatch,
|
||||
findDeploymentMapping,
|
||||
}
|
||||
Reference in new issue
Block a user