OpenViking
volcengine
Self-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:TuringCorp-net/mosaic_compress
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
Stateless dialogue compression based on natural forgetting curve.
LLM conversations grow linearly. MosaicCompress keeps them bounded — automatically, invisibly, and without the user ever knowing what a "Session" is.
Your message array (R rounds, oldest → newest):
Round 1 ────→ Round (R-50) │ Heavy zone → ALL → 2 msgs
Round (R-49) → Round (R-30) │ Light zone → structural truncation, count unchanged
Round (R-29) ──→ Round R │ Raw zone → keep as-is
Steady state: constant message count — 2 + heavyStart × (messages per round), e.g. 102 messages for pure two-message rounds, whether at round 60 or round 15,000 (higher, but still constant, when tool-call rounds add messages). The compression ratio approaches 100%.
The industry-standard answer to unbounded conversations is threshold summarization: when the window fills up, summarize everything into one brief and hand it to a fresh model. The conversation looks like it continues. But structurally it is amnesia followed by reading a diary:
MosaicCompress models the opposite: biological forgetting. A human does not remember round 3 of a 300-round conversation — they keep the lesson, the rules, the relationship. The algorithm reproduces that curve inside one message array:
recent 30 rounds → verbatim (vivid — what you are actually working on)
rounds 30–50 → structural truncation (reasoning/args/results trimmed, text kept)
rounds 50+ → one heavy pair: identity, environment, permissions, rules
No switch moment, no reset, no length limit. The heavy zone is semantic memory (rules that must never be forgotten); the middle is recent episodic memory; the raw zone is the vivid present. Loss is visible: the zone structure tells the model what it no longer knows, so it can fetch detail from shadowed storage on demand.
| Threshold summarization (industry) | MosaicCompress | |
|---|---|---|
| Metaphor | amnesia + diary | continuous vivid memory |
| Continuity | resets on every compaction | never resets |
| Loss | indiscriminate, invisible | graduated, visible |
| Recent turns | paraphrased at the worst moment | always verbatim |
| Purpose | portable handover brief | unbounded human–AI dialogue |
The two philosophies complement each other: a handover brief serves cold starts and long pauses; MosaicCompress serves staying in the conversation. Combined with a durable host-side store (e.g. a MEMORY.md file), human and AI keep talking under the same forgetting curve indefinitely. See docs/design.md §8/§10 for the formal position-is-age model behind this design.
npm install mosaic-compress
import { mosaicCompress, type MosaicConfig } from 'mosaic-compress';
const config: MosaicConfig = {
lightStart: 30, // keep 30 most recent rounds raw
lightWindow: 10, // compress every 10 rounds
heavyStart: 50, // rounds before this get heavy compression
heavyWindow: 10, // same cadence as light
callLLM: async (systemPrompt, userInput) => {
// Wire to OpenAI, Anthropic, or any LLM provider
const res = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userInput },
],
});
return res.choices[0].message.content ?? '';
},
};
// Call every turn — zero cost below threshold; structural light is millisecond-fast,
// Heavy folds take ~1-2s (one LLM summary call)
const compressed = await mosaicCompress(messages, config);
callLLM function for Heavy (OpenAI, Anthropic, local models…); light runs zero-LLMmosaicCompress(messages, config)| Param | Type | Description |
|---|---|---|
messages |
Message[] |
Full message array. System prompt at [0] is preserved as-is. |
config |
MosaicConfig |
Compression config (see below). |
| Returns | Promise<Message[]> |
Compressed message array. |
MosaicConfig| Field | Type | Default | Description |
|---|---|---|---|
lightStart |
number |
30 |
Most recent N rounds kept raw |
lightWindow |
number |
10 |
Anti-jitter: compress every N rounds |
heavyStart |
number |
50 |
Rounds beyond this → Heavy zone |
heavyWindow |
number |
10 |
Anti-jitter for heavy compression |
callLLM |
(sys: string, user: string) => Promise<string> |
optional | Your LLM call function — Heavy zone only; light is structural truncation. Omit it for light-only usage |
onCompress |
(event: CompressEvent) => void \| Promise<void> |
optional | Hook after each compression; receives the original payload for host-side archiving |
DEFAULT_CONFIGPrefer starting from the exported defaults and overriding only what you need:
import { mosaicCompress, DEFAULT_CONFIG, type MosaicConfig } from 'mosaic-compress';
const config: MosaicConfig = { ...DEFAULT_CONFIG, callLLM: async (sys, user) => { /* ... */ } };
All numeric fields must be positive integers (windows) / non-negative integers (starts),
and heavyStart must be greater than lightStart. Invalid configs throw a TypeError.
Messageinterface Message {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
tool_call_id?: string;
tool_calls?: { id: string; type: 'function'; function: { name: string; arguments: string } }[];
}
Read the full design document (English) or 中文设计文档.
MosaicCompress is intentionally stateless and lossy:
onCompress callback hands every compressed-away original to
the host for archiving).MosaicCompress is host-agnostic and works wherever a callLLM function
exists. Its primary integration reference is DeepSeek Harness (DSH)
(deepseek-ai/deepseek-harness
— everything is a plugin), whose task-level compaction / output retention /
spill complement this library's message-level compression (roles and order
preserved). A ready-to-use DSH plugin backend lives in
dsh-module/ (design docs in EN/中文).
Related:
See the Roadmap for upcoming work.
A deterministic simulation (zero LLM cost, reproducible) runs the real algorithm with a rule-based pseudo-LLM. Latest sweep (default parameters):
| Rounds | msgs in | msgs out | tokens in | tokens out | ratio | facts kept |
|---|---|---|---|---|---|---|
| 100 | 234 | 120 | 9,451 | 4,580 | 51.5% | 100% |
| 1,000 | 2,310 | 122 | 91,869 | 5,523 | 94.0% | 100% |
| 5,000 | 11,500 | 120 | 457,484 | 9,913 | 97.8% | 100% |
npm run bench # synthetic sweep: 100 / 500 / 1000 / 5000 rounds
npm run bench -- --file chat.json # analyze your own conversation file
The file mode accepts any JSON array of messages in the library's
Message shape and reports the compression ratio:
[{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}]
See benchmark/README.md for the full method, data
generation, findings, limitations, and the real-LLM spot check
(npm run bench:real — DeepSeek V4 Flash, <$0.01, 5/5 facts retained).
# Run tests (zero LLM cost — uses mock responses)
npm test
# Type-check the whole project
npm run typecheck
# Or directly:
npx tsx tests/index.test.ts
MIT — TuringCorp | iAsk@turingcorp.net
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: agent-memory、context-compression、context-management、context-window、conversation、developer-tools、dsh-memory、memory-management、session-management。