feat: preserve pages and compress context

This commit is contained in:
mesalogo
2026-08-15 20:44:47 +08:00
parent 1031ea618b
commit c2b9ce4ff0
23 changed files with 1972 additions and 507 deletions
+8
View File
@@ -147,6 +147,14 @@ export const conversationMessageSchema = z
createdAt: z.number().int().nonnegative(),
state: z.enum(['streaming', 'complete', 'error']),
status: z.string().max(4_000).optional(),
contextCompression: z
.object({
state: z.enum(['compressing', 'completed', 'failed']),
estimatedBeforeTokens: z.number().int().nonnegative(),
estimatedAfterTokens: z.number().int().nonnegative().optional()
})
.strict()
.optional(),
tools: z.array(conversationToolActivitySchema).max(100).optional(),
sources: z.array(z.string().max(8_192)).max(100).optional(),
sourceReferences: z
+63
View File
@@ -0,0 +1,63 @@
export const minimumModelContextWindowTokens = 32_000
export const maximumModelContextWindowTokens = 10_000_000
export const contextOutputAndSafetyTokens = 12_000
export const estimatedContextRequestOverheadTokens = 4_000
export type ContextWindowMessage = {
role: 'user' | 'assistant'
content: string
}
export function estimateTextTokens(value: string): number {
let asciiCharacters = 0
let nonAsciiCharacters = 0
for (const character of value) {
if (character.codePointAt(0)! <= 0x7f) {
asciiCharacters += 1
} else {
nonAsciiCharacters += 1
}
}
return Math.max(
1,
Math.ceil(asciiCharacters / 4 + nonAsciiCharacters)
)
}
export function estimateMessagesTokens(
messages: readonly ContextWindowMessage[]
): number {
return messages.reduce(
(total, message) => total + estimateTextTokens(message.content) + 4,
0
)
}
export function estimateContextInputTokens(input: {
history: readonly ContextWindowMessage[]
prompt: string
summaryTokens?: number
}): number {
return (
estimateMessagesTokens(input.history) +
estimateTextTokens(input.prompt) +
(input.summaryTokens ?? 0) +
estimatedContextRequestOverheadTokens
)
}
export function getEffectiveContextTriggerTokens(input: {
triggerTokens: number
contextWindowTokens?: number
}): number {
if (input.contextWindowTokens === undefined) {
return input.triggerTokens
}
return Math.min(
input.triggerTokens,
Math.max(
input.contextWindowTokens,
minimumModelContextWindowTokens
) - contextOutputAndSafetyTokens
)
}
+34 -2
View File
@@ -1,4 +1,8 @@
import { z } from 'zod'
import {
maximumModelContextWindowTokens,
minimumModelContextWindowTokens
} from './context-window'
import type {
BrowserProfileCreateInput,
BrowserProfileRenameInput,
@@ -416,6 +420,11 @@ const modelApiKeyUpdateSchema = z.discriminatedUnion('action', [
z.object({ action: z.literal('clear') }).strict()
])
export {
maximumModelContextWindowTokens,
minimumModelContextWindowTokens
} from './context-window'
const modelProfileInputSchema = z
.object({
id: modelProfileIdSchema,
@@ -433,8 +442,8 @@ const modelProfileInputSchema = z
contextWindowTokens: z
.number()
.int()
.min(8_000)
.max(10_000_000)
.min(minimumModelContextWindowTokens)
.max(maximumModelContextWindowTokens)
.optional(),
imageGenerationQuality: imageGenerationQualitySchema,
apiKey: modelApiKeyUpdateSchema
@@ -907,6 +916,29 @@ export type AgentEvent =
type: 'reasoning'
delta: string
}
| {
requestId: string
type: 'context-metrics'
estimatedInputTokens: number
effectiveTriggerTokens: number
contextWindowTokens?: number
compressionEnabled: boolean
recentRawTokens: number
coveredMessageCount: number
summaryTokens: number
}
| {
requestId: string
type: 'context-compression'
state: 'started' | 'completed'
estimatedBeforeTokens: number
estimatedAfterTokens?: number
effectiveTriggerTokens: number
contextWindowTokens?: number
recentRawTokens: number
coveredMessageCount: number
summaryTokens?: number
}
| {
requestId: string
type: 'tool'