Skip to content

Commit 9387b70

Browse files
feat: add encode_delta_with_session for stacked delta+dedup
Delta and session dedup now compose: added symbols that were previously transmitted in earlier calls become bare references in delta output.
1 parent 688b560 commit 9387b70

2 files changed

Lines changed: 84 additions & 1 deletion

File tree

src/gcf/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
from .constants import KIND_ABBREV, KIND_EXPAND
3838
from .decode import DecodeError, decode
39-
from .delta import encode_delta
39+
from .delta import encode_delta, encode_delta_with_session
4040
from .encode import encode
4141
from .generic import encode_generic, GenericOptions
4242
from .session import Session, encode_with_session
@@ -61,6 +61,7 @@
6161
"decode_generic",
6262
"encode",
6363
"encode_delta",
64+
"encode_delta_with_session",
6465
"encode_generic",
6566
"GenericOptions",
6667
"encode_with_session",

src/gcf/delta.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""GCF delta encoding: only added/removed symbols for incremental delivery."""
22

3+
from __future__ import annotations
4+
35
from .constants import KIND_ABBREV
46
from .types import DeltaPayload
57

@@ -52,3 +54,83 @@ def encode_delta(d: DeltaPayload) -> str:
5254
parts.append(f"{e.source} -> {e.target} {e.edge_type}")
5355

5456
return "\n".join(parts) + "\n"
57+
58+
59+
def encode_delta_with_session(
60+
d: DeltaPayload,
61+
sess: "Session | None" = None,
62+
) -> str:
63+
"""Encode a DeltaPayload with session dedup for added symbols.
64+
65+
Stacks delta and session dedup: symbols that are "added" relative to the
66+
previous payload but were transmitted in an earlier call become bare
67+
references instead of full declarations. After encoding, new symbols
68+
are recorded in the session.
69+
70+
Args:
71+
d: The delta payload to encode.
72+
sess: Optional session tracker. If None, falls back to encode_delta.
73+
74+
Returns:
75+
GCF delta-formatted text string.
76+
"""
77+
if sess is None:
78+
return encode_delta(d)
79+
80+
from .session import Session
81+
82+
parts: list[str] = []
83+
84+
savings = 0.0
85+
if d.full_tokens > 0:
86+
savings = 100.0 * (1.0 - d.delta_tokens / d.full_tokens)
87+
88+
parts.append(
89+
f"GCF profile=graph tool={d.tool} delta=true base_root={d.base_root} "
90+
f"new_root={d.new_root} tokens={d.delta_tokens} savings={savings:.0f}% session=true"
91+
)
92+
93+
# Removed symbols.
94+
if d.removed:
95+
parts.append("## removed")
96+
for s in d.removed:
97+
kind = KIND_ABBREV.get(s.kind, s.kind)
98+
parts.append(f"{kind} {s.qualified_name}")
99+
100+
# Added symbols: session-aware. Previously transmitted become bare refs.
101+
new_symbols = []
102+
bare_refs = []
103+
for s in d.added:
104+
if sess.transmitted(s.qualified_name):
105+
bare_refs.append(s)
106+
else:
107+
new_symbols.append(s)
108+
109+
if bare_refs:
110+
parts.append(f"## ref [{len(bare_refs)}]")
111+
for s in bare_refs:
112+
sid = sess.get_id(s.qualified_name)
113+
parts.append(f"@{sid} # previously transmitted")
114+
115+
if new_symbols:
116+
parts.append("## added")
117+
for i, s in enumerate(new_symbols):
118+
kind = KIND_ABBREV.get(s.kind, s.kind)
119+
parts.append(f"@{i} {kind} {s.qualified_name} {s.score:.2f} {s.provenance}")
120+
121+
# Removed edges.
122+
if d.removed_edges:
123+
parts.append("## edges_removed")
124+
for e in d.removed_edges:
125+
parts.append(f"{e.source} -> {e.target} {e.edge_type}")
126+
127+
# Added edges.
128+
if d.added_edges:
129+
parts.append("## edges_added")
130+
for e in d.added_edges:
131+
parts.append(f"{e.source} -> {e.target} {e.edge_type}")
132+
133+
# Record new symbols in session.
134+
sess.record(new_symbols)
135+
136+
return "\n".join(parts) + "\n"

0 commit comments

Comments
 (0)