lcode-agent 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lcode/__init__.py +3 -0
- lcode/__main__.py +3 -0
- lcode/app.py +4140 -0
- lcode/compress.py +80 -0
- lcode/default_config.json +10 -0
- lcode/protocol.py +800 -0
- lcode/settings.py +229 -0
- lcode/store.py +889 -0
- lcode/tools.py +1147 -0
- lcode_agent-0.1.0.dist-info/METADATA +123 -0
- lcode_agent-0.1.0.dist-info/RECORD +13 -0
- lcode_agent-0.1.0.dist-info/WHEEL +4 -0
- lcode_agent-0.1.0.dist-info/entry_points.txt +2 -0
lcode/compress.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""上下文达到模型窗口的 auto-compact 百分比时,把旧对话折成摘要。
|
|
2
|
+
|
|
3
|
+
和 Grok Build 一样:磁盘保留全文,发给模型的是摘要 + 近期原文。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from lcode.protocol import ask
|
|
9
|
+
from lcode.store import Store, _est_tokens
|
|
10
|
+
|
|
11
|
+
_SUMMARIZE_HINT = (
|
|
12
|
+
"把下面的对话和旧摘要压缩成后续助手必须记住的上下文。"
|
|
13
|
+
"保留目标、已确定的事实、约定、人名地名和数字,去掉寒暄与重复。"
|
|
14
|
+
"只用中文正文,不要标题,不要解释你在做什么。\n\n"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _clip(text: str, limit: int) -> str:
|
|
19
|
+
if _est_tokens(text) <= limit:
|
|
20
|
+
return text
|
|
21
|
+
# 粗估:汉字约 1 token,多留一点余量按字符切
|
|
22
|
+
cap = max(32, limit)
|
|
23
|
+
if len(text) <= cap:
|
|
24
|
+
return text
|
|
25
|
+
return text[:cap].rstrip() + "…"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def extractive_summary(old_summary: str, prefix: list, limit: int = 8000) -> str:
|
|
29
|
+
"""不调模型也能压:每条留一句,再卡总长。"""
|
|
30
|
+
parts: list[str] = []
|
|
31
|
+
if old_summary.strip():
|
|
32
|
+
parts.append("【旧摘要】\n" + old_summary.strip())
|
|
33
|
+
for msg in prefix:
|
|
34
|
+
role = getattr(msg, "role", None) or msg.get("role")
|
|
35
|
+
content = getattr(msg, "content", None) or msg.get("content") or ""
|
|
36
|
+
content = " ".join(str(content).split())
|
|
37
|
+
if not content:
|
|
38
|
+
continue
|
|
39
|
+
if role == "user":
|
|
40
|
+
who = "用户"
|
|
41
|
+
elif role == "tool":
|
|
42
|
+
who = "工具"
|
|
43
|
+
else:
|
|
44
|
+
who = "助手"
|
|
45
|
+
line = _clip(f"{who}: {content}", 160)
|
|
46
|
+
parts.append(line)
|
|
47
|
+
return _clip("\n".join(parts), limit)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def llm_summary(blob: str) -> str:
|
|
51
|
+
try:
|
|
52
|
+
text = await ask(_SUMMARIZE_HINT + blob)
|
|
53
|
+
except Exception:
|
|
54
|
+
return ""
|
|
55
|
+
return (text or "").strip()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def prepare_context(
|
|
59
|
+
store: Store,
|
|
60
|
+
session_id: str,
|
|
61
|
+
*,
|
|
62
|
+
force: bool = False,
|
|
63
|
+
hint: str = "",
|
|
64
|
+
) -> list[dict[str, str]]:
|
|
65
|
+
"""若达到窗口百分比阈值(或手动 /zip),压缩旧段后再返回要发给模型的 messages。"""
|
|
66
|
+
if not force and not store.needs_compress(session_id):
|
|
67
|
+
return store.context_for_model(session_id)
|
|
68
|
+
|
|
69
|
+
prefix, _tail = store.split_overflow(session_id, force=force)
|
|
70
|
+
if not prefix:
|
|
71
|
+
return store.context_for_model(session_id)
|
|
72
|
+
|
|
73
|
+
budget = store.summary_budget()
|
|
74
|
+
draft = extractive_summary(store._summary_of(session_id), prefix, budget)
|
|
75
|
+
if hint.strip():
|
|
76
|
+
draft = f"用户要求保留:{hint.strip()}\n\n{draft}"
|
|
77
|
+
polished = await llm_summary(draft)
|
|
78
|
+
summary = _clip(polished or draft, budget)
|
|
79
|
+
store.apply_summary(session_id, summary, prefix[-1].id)
|
|
80
|
+
return store.context_for_model(session_id)
|