fix:将ui改成扁平化ui,优化ui错误
This commit is contained in:
1 parent
d87610151c
commit
d6aa20e85b
36 files changed
+4023
-2973
No files matched your search
@@ -28,4 +28,3 @@ export type AppEnv = {
|
||||
Bindings: Bindings;
|
||||
Variables: Variables;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createMiddleware } from "hono/factory";
|
||||
import type { AppEnv } from "../env";
|
||||
import { getCacheConfig } from "../services/settings";
|
||||
import { canConsumeQuota, normalizeQuota } from "../services/quota";
|
||||
import { getCacheConfig } from "../services/settings";
|
||||
import { withJsonCache } from "../utils/cache";
|
||||
import { sha256Hex } from "../utils/crypto";
|
||||
import { jsonError } from "../utils/http";
|
||||
import { getBearerToken } from "../utils/request";
|
||||
import { withJsonCache } from "../utils/cache";
|
||||
|
||||
export type TokenRecord = {
|
||||
id: string;
|
||||
@@ -37,10 +37,9 @@ export const tokenAuth = createMiddleware<AppEnv>(async (c, next) => {
|
||||
enabled: cacheConfig.enabled,
|
||||
},
|
||||
async () => {
|
||||
const row = await c.env.DB
|
||||
.prepare(
|
||||
"SELECT id, name, quota_total, quota_used, status, allowed_channels, expires_at FROM tokens WHERE key_hash = ?",
|
||||
)
|
||||
const row = await c.env.DB.prepare(
|
||||
"SELECT id, name, quota_total, quota_used, status, allowed_channels, expires_at FROM tokens WHERE key_hash = ?",
|
||||
)
|
||||
.bind(tokenHash)
|
||||
.first<TokenRecord>();
|
||||
return row ?? null;
|
||||
|
||||
@@ -37,7 +37,10 @@ models.get("/", async (c) => {
|
||||
>();
|
||||
for (const entry of entries) {
|
||||
const existing = map.get(entry.id) ?? { id: entry.id, channels: [] };
|
||||
existing.channels.push({ id: entry.channelId, name: entry.channelName });
|
||||
existing.channels.push({
|
||||
id: entry.channelId,
|
||||
name: entry.channelName,
|
||||
});
|
||||
map.set(entry.id, existing);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
fetchChannelModels,
|
||||
updateChannelTestResult,
|
||||
} from "../services/channel-testing";
|
||||
import { bumpCacheVersions } from "../services/settings";
|
||||
import {
|
||||
mergeMetadata,
|
||||
modelsToJson,
|
||||
@@ -29,6 +28,7 @@ import {
|
||||
toNewApiChannel,
|
||||
withNewApiDefaults,
|
||||
} from "../services/newapi";
|
||||
import { bumpCacheVersions } from "../services/settings";
|
||||
import { generateToken } from "../utils/crypto";
|
||||
import { safeJsonParse } from "../utils/json";
|
||||
import { newApiFailure, newApiSuccess } from "../utils/newapi-response";
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Hono } from "hono";
|
||||
import type { AppEnv } from "../env";
|
||||
import { newApiAuth } from "../middleware/newApiAuth";
|
||||
import {
|
||||
listModelsByChannelWithFallback,
|
||||
} from "../services/channel-model-capabilities";
|
||||
import { listModelsByChannelWithFallback } from "../services/channel-model-capabilities";
|
||||
import { listActiveChannels } from "../services/channel-repo";
|
||||
import { newApiSuccess } from "../utils/newapi-response";
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Hono } from "hono";
|
||||
import type { AppEnv } from "../env";
|
||||
import { type TokenRecord, tokenAuth } from "../middleware/tokenAuth";
|
||||
import {
|
||||
type CallTokenItem,
|
||||
} from "../services/call-token-selector";
|
||||
import type { CallTokenItem } from "../services/call-token-selector";
|
||||
import { listCallTokens } from "../services/channel-call-token-repo";
|
||||
import {
|
||||
type ChannelMetadata,
|
||||
@@ -11,20 +9,16 @@ import {
|
||||
resolveMappedModel,
|
||||
resolveProvider,
|
||||
} from "../services/channel-metadata";
|
||||
import {
|
||||
type ChannelRecord,
|
||||
createWeightedOrder,
|
||||
extractModels,
|
||||
} from "../services/channels";
|
||||
import {
|
||||
listCoolingDownChannelsForModel,
|
||||
listVerifiedModelsByChannel,
|
||||
} from "../services/channel-model-capabilities";
|
||||
import {
|
||||
getCacheConfig,
|
||||
getModelFailureCooldownMinutes,
|
||||
getProxyRuntimeSettings,
|
||||
} from "../services/settings";
|
||||
type ChannelRecord,
|
||||
createWeightedOrder,
|
||||
extractModels,
|
||||
} from "../services/channels";
|
||||
import { adaptChatResponse } from "../services/chat-response-adapter";
|
||||
import {
|
||||
applyGeminiModelToPath,
|
||||
buildUpstreamChatRequest,
|
||||
@@ -42,16 +36,24 @@ import {
|
||||
parseDownstreamModel,
|
||||
parseDownstreamStream,
|
||||
} from "../services/provider-transform";
|
||||
import { processUsageQueueEvent, type UsageQueueEvent } from "../services/usage-queue";
|
||||
import {
|
||||
getCacheConfig,
|
||||
getModelFailureCooldownMinutes,
|
||||
getProxyRuntimeSettings,
|
||||
} from "../services/settings";
|
||||
import {
|
||||
getUsageLimiterStub,
|
||||
reserveUsageQueue,
|
||||
} from "../services/usage-limiter";
|
||||
import {
|
||||
processUsageQueueEvent,
|
||||
type UsageQueueEvent,
|
||||
} from "../services/usage-queue";
|
||||
import { withJsonCache } from "../utils/cache";
|
||||
import { jsonError } from "../utils/http";
|
||||
import { safeJsonParse } from "../utils/json";
|
||||
import { extractReasoningEffort } from "../utils/reasoning";
|
||||
import { normalizeBaseUrl } from "../utils/url";
|
||||
import { withJsonCache } from "../utils/cache";
|
||||
import {
|
||||
type NormalizedUsage,
|
||||
parseUsageFromHeaders,
|
||||
@@ -60,7 +62,6 @@ import {
|
||||
type StreamUsageMode,
|
||||
type StreamUsageOptions,
|
||||
} from "../utils/usage";
|
||||
import { adaptChatResponse } from "../services/chat-response-adapter";
|
||||
|
||||
const proxy = new Hono<AppEnv>();
|
||||
|
||||
@@ -100,12 +101,10 @@ function normalizeMessage(value: string | null): string | null {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function getStreamUsageOptions(
|
||||
settings: {
|
||||
stream_usage_mode: string;
|
||||
stream_usage_max_bytes: number;
|
||||
},
|
||||
): StreamUsageOptions {
|
||||
function getStreamUsageOptions(settings: {
|
||||
stream_usage_mode: string;
|
||||
stream_usage_max_bytes: number;
|
||||
}): StreamUsageOptions {
|
||||
return {
|
||||
mode: settings.stream_usage_mode as StreamUsageMode,
|
||||
maxBytes: Math.max(0, Math.floor(settings.stream_usage_max_bytes)),
|
||||
@@ -173,7 +172,7 @@ function createUsageEventScheduler(
|
||||
|
||||
const decideSequential = () => {
|
||||
const decision = decisionChain.then(decide);
|
||||
decisionChain = decision.catch(() => undefined);
|
||||
decisionChain = decision.then(() => undefined).catch(() => undefined);
|
||||
return decision;
|
||||
};
|
||||
|
||||
@@ -198,7 +197,10 @@ async function extractErrorDetails(
|
||||
): Promise<{ errorCode: string | null; errorMessage: string | null }> {
|
||||
const contentType = response.headers.get("content-type") ?? "";
|
||||
if (contentType.includes("application/json")) {
|
||||
const payload = await response.clone().json().catch(() => null);
|
||||
const payload = await response
|
||||
.clone()
|
||||
.json()
|
||||
.catch(() => null);
|
||||
if (payload && typeof payload === "object") {
|
||||
const raw = payload as Record<string, unknown>;
|
||||
const error = (raw.error ?? raw) as Record<string, unknown>;
|
||||
@@ -220,7 +222,10 @@ async function extractErrorDetails(
|
||||
};
|
||||
}
|
||||
}
|
||||
const text = await response.clone().text().catch(() => "");
|
||||
const text = await response
|
||||
.clone()
|
||||
.text()
|
||||
.catch(() => "");
|
||||
return { errorCode: null, errorMessage: normalizeMessage(text) };
|
||||
}
|
||||
|
||||
@@ -560,8 +565,9 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
enabled: cacheConfig.enabled,
|
||||
},
|
||||
async () => {
|
||||
const result = await c.env.DB
|
||||
.prepare("SELECT * FROM channels WHERE status = ?")
|
||||
const result = await c.env.DB.prepare(
|
||||
"SELECT * FROM channels WHERE status = ?",
|
||||
)
|
||||
.bind("active")
|
||||
.all();
|
||||
return (result.results ?? []) as ChannelRecord[];
|
||||
@@ -631,12 +637,7 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
code: "upstream_cooldown",
|
||||
message: "upstream_cooldown",
|
||||
});
|
||||
return jsonError(
|
||||
c,
|
||||
503,
|
||||
"upstream_cooldown",
|
||||
"upstream_cooldown",
|
||||
);
|
||||
return jsonError(c, 503, "upstream_cooldown", "upstream_cooldown");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -782,10 +783,7 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
endpointType === "images" &&
|
||||
metadata.endpoint_overrides.image_url
|
||||
) {
|
||||
if (endpointType === "images" && metadata.endpoint_overrides.image_url) {
|
||||
if (normalizedImage) {
|
||||
const request = buildUpstreamImageRequest(
|
||||
upstreamProvider,
|
||||
@@ -989,7 +987,8 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
} catch (error) {
|
||||
const isTimeout =
|
||||
error instanceof Error &&
|
||||
(error.name === "AbortError" || error.message.includes("upstream_timeout"));
|
||||
(error.name === "AbortError" ||
|
||||
error.message.includes("upstream_timeout"));
|
||||
console.error("[proxy:upstream_exception]", {
|
||||
channel_id: channel.id,
|
||||
upstream_provider: upstreamProvider,
|
||||
@@ -1085,11 +1084,11 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
const errorDetails =
|
||||
lastResponse.ok || selectedChannel
|
||||
? null
|
||||
: lastErrorDetails ?? {
|
||||
: (lastErrorDetails ?? {
|
||||
upstreamStatus: lastResponse.status,
|
||||
errorCode: null,
|
||||
errorMessage: null,
|
||||
};
|
||||
});
|
||||
const record = async (
|
||||
usage: NormalizedUsage | null,
|
||||
firstTokenLatencyMs?: number | null,
|
||||
@@ -1105,14 +1104,14 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
const usageMissing = !usage;
|
||||
const errorCode = lastResponse.ok
|
||||
? usageMissing
|
||||
? usageMissingReason ?? "usage_missing"
|
||||
? (usageMissingReason ?? "usage_missing")
|
||||
: null
|
||||
: errorDetails?.errorCode ?? null;
|
||||
: (errorDetails?.errorCode ?? null);
|
||||
const errorMessage = lastResponse.ok
|
||||
? usageMissing
|
||||
? usageMissingReason ?? "usage_missing"
|
||||
? (usageMissingReason ?? "usage_missing")
|
||||
: null
|
||||
: errorDetails?.errorMessage ?? null;
|
||||
: (errorDetails?.errorMessage ?? null);
|
||||
scheduleUsageEvent({
|
||||
type: "usage",
|
||||
payload: {
|
||||
@@ -1131,7 +1130,7 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
status: lastResponse.ok ? "ok" : "error",
|
||||
upstreamStatus: lastResponse.ok
|
||||
? lastResponse.status
|
||||
: errorDetails?.upstreamStatus ?? lastResponse.status,
|
||||
: (errorDetails?.upstreamStatus ?? lastResponse.status),
|
||||
errorCode,
|
||||
errorMessage,
|
||||
},
|
||||
@@ -1167,13 +1166,15 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
const task = parseUsageFromSse(lastResponse.clone(), streamUsageOptions)
|
||||
.then((streamUsage) => {
|
||||
const usageValue = immediateUsage ?? streamUsage.usage;
|
||||
const reason =
|
||||
usageValue ? null : "usage_missing";
|
||||
const reason = usageValue ? null : "usage_missing";
|
||||
return record(usageValue, streamUsage.firstTokenLatencyMs, reason);
|
||||
})
|
||||
.catch(() => undefined)
|
||||
.finally(() => {
|
||||
activeStreamUsageParsers = Math.max(0, activeStreamUsageParsers - 1);
|
||||
activeStreamUsageParsers = Math.max(
|
||||
0,
|
||||
activeStreamUsageParsers - 1,
|
||||
);
|
||||
});
|
||||
if (executionCtx?.waitUntil) {
|
||||
executionCtx.waitUntil(task);
|
||||
@@ -1182,7 +1183,11 @@ proxy.all("/*", tokenAuth, async (c) => {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await record(immediateUsage, latencyMs, immediateUsage ? null : "usage_missing");
|
||||
await record(
|
||||
immediateUsage,
|
||||
latencyMs,
|
||||
immediateUsage ? null : "usage_missing",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,19 +8,19 @@ import {
|
||||
bumpCacheVersions,
|
||||
getCacheConfig,
|
||||
getCheckinScheduleTime,
|
||||
getRetentionDays,
|
||||
getSessionTtlHours,
|
||||
getModelFailureCooldownMinutes,
|
||||
getProxyRuntimeSettings,
|
||||
getRetentionDays,
|
||||
getRuntimeProxyConfig,
|
||||
getSessionTtlHours,
|
||||
isAdminPasswordSet,
|
||||
setCacheConfig,
|
||||
setAdminPasswordHash,
|
||||
setCacheConfig,
|
||||
setCheckinScheduleTime,
|
||||
setRetentionDays,
|
||||
setSessionTtlHours,
|
||||
setModelFailureCooldownMinutes,
|
||||
setProxyRuntimeSettings,
|
||||
setRetentionDays,
|
||||
setSessionTtlHours,
|
||||
} from "../services/settings";
|
||||
import {
|
||||
getUsageLimiterStub,
|
||||
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
updateChannel,
|
||||
} from "../services/channel-repo";
|
||||
import { runCheckinAll, runCheckinSingle } from "../services/checkin-runner";
|
||||
import { bumpCacheVersions } from "../services/settings";
|
||||
import {
|
||||
buildSiteMetadata,
|
||||
parseSiteMetadata,
|
||||
type SiteType,
|
||||
} from "../services/site-metadata";
|
||||
import { bumpCacheVersions } from "../services/settings";
|
||||
import { generateToken } from "../utils/crypto";
|
||||
import { jsonError } from "../utils/http";
|
||||
import { nowIso } from "../utils/time";
|
||||
|
||||
@@ -20,9 +20,7 @@ type TokenRow = {
|
||||
expires_at?: string | null;
|
||||
};
|
||||
|
||||
const normalizeAllowedChannels = (
|
||||
raw: string | null,
|
||||
): string[] | null => {
|
||||
const normalizeAllowedChannels = (raw: string | null): string[] | null => {
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,9 @@ usage.get("/", async (c) => {
|
||||
}
|
||||
|
||||
const rawLimit = Number(query.limit ?? 50);
|
||||
const normalizedLimit = Number.isNaN(rawLimit) ? 50 : Math.floor(rawLimit);
|
||||
const normalizedLimit = Number.isNaN(rawLimit)
|
||||
? 50
|
||||
: Math.floor(rawLimit);
|
||||
const limit = Math.min(Math.max(normalizedLimit, 1), 200);
|
||||
const rawOffset = Number(query.offset ?? 0);
|
||||
const normalizedOffset = Number.isNaN(rawOffset)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { D1Database } from "@cloudflare/workers-types";
|
||||
import { nowIso } from "../utils/time";
|
||||
import { extractModelIds } from "./channel-models";
|
||||
import type { ModelEntry } from "./channel-models";
|
||||
import { extractModelIds } from "./channel-models";
|
||||
|
||||
export type CapabilityRow = {
|
||||
channel_id: string;
|
||||
@@ -51,7 +51,9 @@ export async function listVerifiedModelEntries(
|
||||
channels: Array<{ id: string; name: string }>,
|
||||
): Promise<ModelEntry[]> {
|
||||
const ids = channels.map((channel) => channel.id);
|
||||
const nameMap = new Map(channels.map((channel) => [channel.id, channel.name]));
|
||||
const nameMap = new Map(
|
||||
channels.map((channel) => [channel.id, channel.name]),
|
||||
);
|
||||
const map = await listVerifiedModelsByChannel(db, ids);
|
||||
const entries: ModelEntry[] = [];
|
||||
for (const [channelId, models] of map.entries()) {
|
||||
@@ -96,7 +98,12 @@ export async function listModelEntriesWithFallback(
|
||||
continue;
|
||||
}
|
||||
for (const id of models) {
|
||||
entries.push({ id, label: id, channelId: channel.id, channelName: channel.name });
|
||||
entries.push({
|
||||
id,
|
||||
label: id,
|
||||
channelId: channel.id,
|
||||
channelName: channel.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
@@ -153,14 +160,7 @@ export async function recordChannelModelError(
|
||||
.prepare(
|
||||
"INSERT INTO channel_model_capabilities (channel_id, model, last_ok_at, last_err_at, last_err_code, last_err_count, created_at, updated_at) VALUES (?, ?, 0, ?, ?, 1, ?, ?) ON CONFLICT(channel_id, model) DO UPDATE SET last_err_at = excluded.last_err_at, last_err_code = excluded.last_err_code, last_err_count = COALESCE(channel_model_capabilities.last_err_count, 0) + 1, updated_at = excluded.updated_at",
|
||||
)
|
||||
.bind(
|
||||
channelId,
|
||||
model,
|
||||
nowSeconds,
|
||||
errorCode,
|
||||
timestamp,
|
||||
timestamp,
|
||||
)
|
||||
.bind(channelId, model, nowSeconds, errorCode, timestamp, timestamp)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { D1Database } from "@cloudflare/workers-types";
|
||||
import { nowIso } from "../utils/time";
|
||||
import { normalizeBaseUrl } from "../utils/url";
|
||||
import { modelsToJson, normalizeModelsInput } from "./channel-models";
|
||||
import { upsertChannelModelCapabilities } from "./channel-model-capabilities";
|
||||
import { modelsToJson, normalizeModelsInput } from "./channel-models";
|
||||
|
||||
export type ChannelTestResult = {
|
||||
ok: boolean;
|
||||
|
||||
@@ -260,7 +260,9 @@ function writeGeminiChunk(
|
||||
controller.enqueue(encoder.encode(`${JSON.stringify(data)}\n`));
|
||||
}
|
||||
|
||||
async function adaptOpenAiJsonToAnthropic(options: AdaptOptions): Promise<Response> {
|
||||
async function adaptOpenAiJsonToAnthropic(
|
||||
options: AdaptOptions,
|
||||
): Promise<Response> {
|
||||
const payload = (await options.response
|
||||
.clone()
|
||||
.json()
|
||||
@@ -283,7 +285,9 @@ async function adaptOpenAiJsonToAnthropic(options: AdaptOptions): Promise<Respon
|
||||
: {};
|
||||
const promptTokens = Number(usage.prompt_tokens ?? 0) || 0;
|
||||
const completionTokens = Number(usage.completion_tokens ?? 0) || 0;
|
||||
const stopReason = mapOpenAiFinishReasonToAnthropic(firstChoice.finish_reason);
|
||||
const stopReason = mapOpenAiFinishReasonToAnthropic(
|
||||
firstChoice.finish_reason,
|
||||
);
|
||||
const text = openAiContentToText(message.content);
|
||||
const transformed = {
|
||||
id:
|
||||
@@ -460,7 +464,9 @@ function adaptOpenAiSseToAnthropic(options: AdaptOptions): Response {
|
||||
});
|
||||
}
|
||||
|
||||
async function adaptAnthropicJsonToOpenAi(options: AdaptOptions): Promise<Response> {
|
||||
async function adaptAnthropicJsonToOpenAi(
|
||||
options: AdaptOptions,
|
||||
): Promise<Response> {
|
||||
const payload = (await options.response
|
||||
.clone()
|
||||
.json()
|
||||
@@ -552,7 +558,8 @@ function adaptAnthropicSseToOpenAi(options: AdaptOptions): Response {
|
||||
newlineIndex = buffer.indexOf("\n");
|
||||
continue;
|
||||
}
|
||||
const eventType = typeof parsed.type === "string" ? parsed.type : "";
|
||||
const eventType =
|
||||
typeof parsed.type === "string" ? parsed.type : "";
|
||||
if (!started && eventType === "message_start") {
|
||||
started = true;
|
||||
writeOpenAiSseChunk(controller, encoder, {
|
||||
@@ -642,7 +649,9 @@ function adaptAnthropicSseToOpenAi(options: AdaptOptions): Response {
|
||||
});
|
||||
}
|
||||
|
||||
async function adaptGeminiJsonToOpenAi(options: AdaptOptions): Promise<Response> {
|
||||
async function adaptGeminiJsonToOpenAi(
|
||||
options: AdaptOptions,
|
||||
): Promise<Response> {
|
||||
const payload = (await options.response
|
||||
.clone()
|
||||
.json()
|
||||
@@ -651,7 +660,9 @@ async function adaptGeminiJsonToOpenAi(options: AdaptOptions): Promise<Response>
|
||||
return options.response;
|
||||
}
|
||||
const text = geminiCandidateText(payload);
|
||||
const finishReason = mapGeminiFinishReasonToOpenAi(geminiFinishReason(payload));
|
||||
const finishReason = mapGeminiFinishReasonToOpenAi(
|
||||
geminiFinishReason(payload),
|
||||
);
|
||||
const usage = geminiUsageTokens(payload);
|
||||
const transformed = {
|
||||
id: `chatcmpl_${Date.now()}`,
|
||||
@@ -680,7 +691,9 @@ async function adaptGeminiJsonToOpenAi(options: AdaptOptions): Promise<Response>
|
||||
});
|
||||
}
|
||||
|
||||
async function adaptGeminiJsonToAnthropic(options: AdaptOptions): Promise<Response> {
|
||||
async function adaptGeminiJsonToAnthropic(
|
||||
options: AdaptOptions,
|
||||
): Promise<Response> {
|
||||
const payload = (await options.response
|
||||
.clone()
|
||||
.json()
|
||||
@@ -712,7 +725,9 @@ async function adaptGeminiJsonToAnthropic(options: AdaptOptions): Promise<Respon
|
||||
});
|
||||
}
|
||||
|
||||
async function adaptOpenAiJsonToGemini(options: AdaptOptions): Promise<Response> {
|
||||
async function adaptOpenAiJsonToGemini(
|
||||
options: AdaptOptions,
|
||||
): Promise<Response> {
|
||||
const payload = (await options.response
|
||||
.clone()
|
||||
.json()
|
||||
@@ -757,7 +772,9 @@ async function adaptOpenAiJsonToGemini(options: AdaptOptions): Promise<Response>
|
||||
});
|
||||
}
|
||||
|
||||
async function adaptAnthropicJsonToGemini(options: AdaptOptions): Promise<Response> {
|
||||
async function adaptAnthropicJsonToGemini(
|
||||
options: AdaptOptions,
|
||||
): Promise<Response> {
|
||||
const payload = (await options.response
|
||||
.clone()
|
||||
.json()
|
||||
@@ -833,7 +850,11 @@ function adaptGeminiSseToOpenAi(options: AdaptOptions): Response {
|
||||
created,
|
||||
model: options.model ?? "",
|
||||
choices: [
|
||||
{ index: 0, delta: { role: "assistant" }, finish_reason: null },
|
||||
{
|
||||
index: 0,
|
||||
delta: { role: "assistant" },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -952,7 +973,9 @@ function adaptGeminiSseToAnthropic(options: AdaptOptions): Response {
|
||||
writeSseEvent(controller, encoder, "message_delta", {
|
||||
type: "message_delta",
|
||||
delta: { stop_reason: stopReason, stop_sequence: null },
|
||||
usage: { output_tokens: geminiUsageTokens(parsed).completionTokens },
|
||||
usage: {
|
||||
output_tokens: geminiUsageTokens(parsed).completionTokens,
|
||||
},
|
||||
});
|
||||
writeSseEvent(controller, encoder, "message_stop", {
|
||||
type: "message_stop",
|
||||
@@ -1051,7 +1074,9 @@ function adaptOpenAiSseToGemini(options: AdaptOptions): Response {
|
||||
}
|
||||
if (!lastFinishReason) {
|
||||
writeGeminiChunk(controller, encoder, {
|
||||
candidates: [{ content: { role: "model", parts: [] }, finishReason: "STOP" }],
|
||||
candidates: [
|
||||
{ content: { role: "model", parts: [] }, finishReason: "STOP" },
|
||||
],
|
||||
});
|
||||
}
|
||||
controller.close();
|
||||
@@ -1096,7 +1121,8 @@ function adaptAnthropicSseToGemini(options: AdaptOptions): Response {
|
||||
newlineIndex = buffer.indexOf("\n");
|
||||
continue;
|
||||
}
|
||||
const eventType = typeof parsed.type === "string" ? parsed.type : "";
|
||||
const eventType =
|
||||
typeof parsed.type === "string" ? parsed.type : "";
|
||||
if (eventType === "content_block_delta") {
|
||||
const delta =
|
||||
parsed.delta && typeof parsed.delta === "object"
|
||||
@@ -1119,7 +1145,8 @@ function adaptAnthropicSseToGemini(options: AdaptOptions): Response {
|
||||
? (parsed.delta as Record<string, unknown>)
|
||||
: {};
|
||||
lastFinishReason =
|
||||
mapAnthropicStopReasonToGemini(delta.stop_reason) ?? lastFinishReason;
|
||||
mapAnthropicStopReasonToGemini(delta.stop_reason) ??
|
||||
lastFinishReason;
|
||||
}
|
||||
newlineIndex = buffer.indexOf("\n");
|
||||
}
|
||||
@@ -1185,7 +1212,9 @@ const adapters: Record<string, AdapterFn> = {
|
||||
},
|
||||
};
|
||||
|
||||
export async function adaptChatResponse(options: AdaptOptions): Promise<Response> {
|
||||
export async function adaptChatResponse(
|
||||
options: AdaptOptions,
|
||||
): Promise<Response> {
|
||||
if (options.upstreamProvider === options.downstreamProvider) {
|
||||
return options.response;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,10 @@ function parsePositiveNumber(value: string | null, fallback: number): number {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function parseNonNegativeSetting(value: string | null, fallback: number): number {
|
||||
function parseNonNegativeSetting(
|
||||
value: string | null,
|
||||
fallback: number,
|
||||
): number {
|
||||
if (!value) {
|
||||
return fallback;
|
||||
}
|
||||
@@ -259,9 +262,7 @@ function clearCacheConfigSnapshot(): void {
|
||||
cacheConfigSnapshot = null;
|
||||
}
|
||||
|
||||
function setSettingsCacheControlSnapshot(
|
||||
value: SettingsCacheControl,
|
||||
): void {
|
||||
function setSettingsCacheControlSnapshot(value: SettingsCacheControl): void {
|
||||
settingsCacheControlSnapshot = {
|
||||
value,
|
||||
expiresAt: Date.now() + CACHE_CONFIG_TTL_MS,
|
||||
|
||||
@@ -77,8 +77,7 @@ export class UsageLimiter {
|
||||
return new Response("Invalid payload", { status: 400 });
|
||||
}
|
||||
const nowDate = beijingDateString(new Date());
|
||||
let storedDate =
|
||||
(await this.state.storage.get<string>(DATE_KEY)) ?? null;
|
||||
let storedDate = (await this.state.storage.get<string>(DATE_KEY)) ?? null;
|
||||
let count = (await this.state.storage.get<number>(COUNT_KEY)) ?? 0;
|
||||
if (storedDate !== nowDate) {
|
||||
storedDate = nowDate;
|
||||
@@ -106,8 +105,7 @@ export class UsageLimiter {
|
||||
}
|
||||
if (request.method === "GET" && url.pathname === "/status") {
|
||||
const nowDate = beijingDateString(new Date());
|
||||
let storedDate =
|
||||
(await this.state.storage.get<string>(DATE_KEY)) ?? null;
|
||||
let storedDate = (await this.state.storage.get<string>(DATE_KEY)) ?? null;
|
||||
let count = (await this.state.storage.get<number>(COUNT_KEY)) ?? 0;
|
||||
if (storedDate !== nowDate) {
|
||||
storedDate = nowDate;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { D1Database, ExecutionContext } from "@cloudflare/workers-types";
|
||||
import type { Bindings } from "../env";
|
||||
import { recordChannelModelError, upsertChannelModelCapabilities } from "./channel-model-capabilities";
|
||||
import {
|
||||
recordChannelModelError,
|
||||
upsertChannelModelCapabilities,
|
||||
} from "./channel-model-capabilities";
|
||||
import type { UsageInput } from "./usage";
|
||||
import { recordUsage } from "./usage";
|
||||
|
||||
|
||||
@@ -18,11 +18,7 @@ export type StreamUsageOptions = {
|
||||
maxBytes?: number;
|
||||
};
|
||||
|
||||
const USAGE_HINTS = [
|
||||
"\"usage\"",
|
||||
"\"usageMetadata\"",
|
||||
"\"usage_metadata\"",
|
||||
];
|
||||
const USAGE_HINTS = ['"usage"', '"usageMetadata"', '"usage_metadata"'];
|
||||
|
||||
function toNumber(value: unknown): number | null {
|
||||
if (value === null || value === undefined) {
|
||||
|
||||
Reference in new issue
Block a user