mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Unsupported image rejections from text-only compatible providers should not poison the session, and low-trust multimodal usage spikes should not make context indicators report a full window. Constraint: Third-party Anthropic-compatible providers may report encoded media bytes as usage tokens. Rejected: Trust all provider usage uniformly | third-party media responses can pin context to 100% incorrectly. Confidence: high Scope-risk: moderate Directive: Do not remove the media-aware fallback without checking text-only provider recovery and desktop context indicators. Tested: bun run check:server Tested: focused media/context regression suite
179 lines
4.5 KiB
TypeScript
179 lines
4.5 KiB
TypeScript
import { getAPIProvider } from './model/providers.js'
|
|
|
|
export type ProviderUsageTrust = 'high' | 'low'
|
|
|
|
export type ContextUsageLike = {
|
|
input_tokens: number
|
|
output_tokens?: number
|
|
cache_creation_input_tokens: number
|
|
cache_read_input_tokens: number
|
|
}
|
|
|
|
export type ContextBudgetSource = 'estimate' | 'provider_usage'
|
|
|
|
export type ContextBudget = {
|
|
usedTokens: number
|
|
source: ContextBudgetSource
|
|
providerUsageTokens: number | null
|
|
estimatedTokens: number
|
|
ignoredUsageReason?: 'low_trust_media_usage'
|
|
}
|
|
|
|
export function getProviderUsageTrust(args: {
|
|
isFirstPartyAnthropic: boolean
|
|
}): ProviderUsageTrust {
|
|
return getAPIProvider() === 'firstParty' && args.isFirstPartyAnthropic
|
|
? 'high'
|
|
: 'low'
|
|
}
|
|
|
|
function isMediaAttachment(attachment: unknown): boolean {
|
|
if (typeof attachment !== 'object' || attachment === null) return false
|
|
|
|
const candidate = attachment as {
|
|
type?: unknown
|
|
content?: { type?: unknown } | null
|
|
}
|
|
|
|
if (candidate.type !== 'file') return false
|
|
|
|
return (
|
|
candidate.content?.type === 'image' || candidate.content?.type === 'pdf'
|
|
)
|
|
}
|
|
|
|
export function hasMediaInput(
|
|
messages: readonly {
|
|
type: string
|
|
message?: { content?: unknown }
|
|
attachment?: unknown
|
|
}[],
|
|
): boolean {
|
|
return messages.some(message => {
|
|
if (message.type === 'attachment') {
|
|
return isMediaAttachment(message.attachment)
|
|
}
|
|
if (message.type !== 'user' && message.type !== 'assistant') return false
|
|
if (!Array.isArray(message.message?.content)) return false
|
|
|
|
return message.message.content.some(
|
|
block =>
|
|
typeof block === 'object' &&
|
|
block !== null &&
|
|
'type' in block &&
|
|
(block.type === 'image' || block.type === 'document'),
|
|
)
|
|
})
|
|
}
|
|
|
|
type ShouldIgnoreLowTrustUsageArgs = {
|
|
usageTrust: ProviderUsageTrust
|
|
hasMediaInput: boolean
|
|
usageTokens: number
|
|
estimatedTokens: number
|
|
contextWindow: number
|
|
}
|
|
|
|
type CalculateContextBudgetArgs = {
|
|
estimatedTokens: number
|
|
contextWindow: number
|
|
currentUsage: ContextUsageLike | null
|
|
usageTrust: ProviderUsageTrust
|
|
hasMediaInput: boolean
|
|
}
|
|
|
|
export function getUsageTokenTotal(
|
|
usage: ContextUsageLike,
|
|
options?: { includeOutput?: boolean },
|
|
): number {
|
|
const includeOutput = options?.includeOutput ?? true
|
|
|
|
return (
|
|
usage.input_tokens +
|
|
usage.cache_creation_input_tokens +
|
|
usage.cache_read_input_tokens +
|
|
(includeOutput ? usage.output_tokens ?? 0 : 0)
|
|
)
|
|
}
|
|
|
|
export function shouldIgnoreLowTrustUsage(
|
|
args: ShouldIgnoreLowTrustUsageArgs,
|
|
): boolean {
|
|
if (args.usageTrust === 'high') return false
|
|
if (!args.hasMediaInput) return false
|
|
if (args.estimatedTokens <= 0) return false
|
|
if (args.usageTokens < args.contextWindow) return false
|
|
if (args.estimatedTokens >= args.contextWindow) return false
|
|
|
|
const suspiciousFloor = Math.max(
|
|
args.estimatedTokens * 4,
|
|
args.estimatedTokens + 50_000,
|
|
)
|
|
return args.usageTokens > suspiciousFloor
|
|
}
|
|
|
|
export function calculateContextBudget(
|
|
args: CalculateContextBudgetArgs,
|
|
): ContextBudget {
|
|
const estimatedTokens = Math.min(args.estimatedTokens, args.contextWindow)
|
|
|
|
if (!args.currentUsage) {
|
|
return {
|
|
usedTokens: estimatedTokens,
|
|
source: 'estimate',
|
|
providerUsageTokens: null,
|
|
estimatedTokens: args.estimatedTokens,
|
|
}
|
|
}
|
|
|
|
const providerUsageTokens = getUsageTokenTotal(args.currentUsage)
|
|
|
|
if (
|
|
shouldIgnoreLowTrustUsage({
|
|
usageTrust: args.usageTrust,
|
|
hasMediaInput: args.hasMediaInput,
|
|
usageTokens: providerUsageTokens,
|
|
estimatedTokens: args.estimatedTokens,
|
|
contextWindow: args.contextWindow,
|
|
})
|
|
) {
|
|
return {
|
|
usedTokens: estimatedTokens,
|
|
source: 'estimate',
|
|
providerUsageTokens,
|
|
estimatedTokens: args.estimatedTokens,
|
|
ignoredUsageReason: 'low_trust_media_usage',
|
|
}
|
|
}
|
|
|
|
const usedTokens = Math.min(
|
|
Math.max(args.estimatedTokens, providerUsageTokens),
|
|
args.contextWindow,
|
|
)
|
|
|
|
return {
|
|
usedTokens,
|
|
source:
|
|
providerUsageTokens >= args.estimatedTokens ? 'provider_usage' : 'estimate',
|
|
providerUsageTokens,
|
|
estimatedTokens: args.estimatedTokens,
|
|
}
|
|
}
|
|
|
|
export function calculateContextPercentagesFromTokens(
|
|
usedTokens: number | null,
|
|
contextWindowSize: number,
|
|
): { used: number | null; remaining: number | null } {
|
|
if (usedTokens === null || contextWindowSize <= 0) {
|
|
return { used: null, remaining: null }
|
|
}
|
|
|
|
const usedPercentage = Math.round((usedTokens / contextWindowSize) * 100)
|
|
const used = Math.min(100, Math.max(0, usedPercentage))
|
|
|
|
return {
|
|
used,
|
|
remaining: 100 - used,
|
|
}
|
|
}
|