dagc 0.1.0__tar.gz
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.
- dagc-0.1.0/LICENSE +21 -0
- dagc-0.1.0/PKG-INFO +225 -0
- dagc-0.1.0/README.md +178 -0
- dagc-0.1.0/pyproject.toml +56 -0
- dagc-0.1.0/setup.cfg +4 -0
- dagc-0.1.0/src/dagc/__init__.py +47 -0
- dagc-0.1.0/src/dagc/adapters.py +93 -0
- dagc-0.1.0/src/dagc/baselines.py +82 -0
- dagc-0.1.0/src/dagc/cli.py +112 -0
- dagc-0.1.0/src/dagc/compressor.py +968 -0
- dagc-0.1.0/src/dagc/config.py +119 -0
- dagc-0.1.0/src/dagc/extraction.py +662 -0
- dagc-0.1.0/src/dagc/formats.py +206 -0
- dagc-0.1.0/src/dagc/graph.py +263 -0
- dagc-0.1.0/src/dagc/schema.py +194 -0
- dagc-0.1.0/src/dagc/utils.py +250 -0
- dagc-0.1.0/src/dagc.egg-info/PKG-INFO +225 -0
- dagc-0.1.0/src/dagc.egg-info/SOURCES.txt +39 -0
- dagc-0.1.0/src/dagc.egg-info/dependency_links.txt +1 -0
- dagc-0.1.0/src/dagc.egg-info/entry_points.txt +3 -0
- dagc-0.1.0/src/dagc.egg-info/requires.txt +36 -0
- dagc-0.1.0/src/dagc.egg-info/top_level.txt +2 -0
- dagc-0.1.0/src/dagc_eval/__init__.py +42 -0
- dagc-0.1.0/src/dagc_eval/adversarial.py +214 -0
- dagc-0.1.0/src/dagc_eval/benchmark.py +375 -0
- dagc-0.1.0/src/dagc_eval/diagnostics.py +139 -0
- dagc-0.1.0/src/dagc_eval/export.py +80 -0
- dagc-0.1.0/src/dagc_eval/interfaces.py +73 -0
- dagc-0.1.0/src/dagc_eval/leaderboard.py +77 -0
- dagc-0.1.0/src/dagc_eval/match.py +238 -0
- dagc-0.1.0/src/dagc_eval/normalize.py +23 -0
- dagc-0.1.0/src/dagc_eval/reproduce.py +318 -0
- dagc-0.1.0/src/dagc_eval/server.py +201 -0
- dagc-0.1.0/src/dagc_eval/stats.py +93 -0
- dagc-0.1.0/tests/test_adversarial.py +21 -0
- dagc-0.1.0/tests/test_artifacts_extraction.py +106 -0
- dagc-0.1.0/tests/test_decision_protection.py +117 -0
- dagc-0.1.0/tests/test_format_robustness.py +257 -0
- dagc-0.1.0/tests/test_optional_adapter_imports.py +33 -0
- dagc-0.1.0/tests/test_schema_adapters.py +49 -0
- dagc-0.1.0/tests/test_stats.py +89 -0
dagc-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shaurya Sawant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dagc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dagc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Decision-Anchored Graph Compression for agent/chat traces: preserve decision-critical artifacts with regex, embeddings, and graph algorithms, support varied message formats, and include optional evaluation tools.
|
|
5
|
+
Author: Shaurya Sawant
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Shauryasawant/DAGC-Decision-Anchored-Graph-Compression
|
|
8
|
+
Project-URL: Repository, https://github.com/Shauryasawant/DAGC-Decision-Anchored-Graph-Compression
|
|
9
|
+
Project-URL: Issues, https://github.com/Shauryasawant/DAGC-Decision-Anchored-Graph-Compression/issues
|
|
10
|
+
Keywords: agent,chat,compression,context,llm,memory
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy>=1.22
|
|
19
|
+
Requires-Dist: scipy>=1.10
|
|
20
|
+
Provides-Extra: tiktoken
|
|
21
|
+
Requires-Dist: tiktoken>=0.5; extra == "tiktoken"
|
|
22
|
+
Provides-Extra: sentence-transformers
|
|
23
|
+
Requires-Dist: sentence-transformers>=2.2; extra == "sentence-transformers"
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai>=1.0; extra == "openai"
|
|
26
|
+
Provides-Extra: cohere
|
|
27
|
+
Requires-Dist: cohere>=5.0; extra == "cohere"
|
|
28
|
+
Provides-Extra: anthropic
|
|
29
|
+
Requires-Dist: anthropic>=0.30; extra == "anthropic"
|
|
30
|
+
Provides-Extra: mistralai
|
|
31
|
+
Requires-Dist: mistralai>=1.0; extra == "mistralai"
|
|
32
|
+
Provides-Extra: server
|
|
33
|
+
Requires-Dist: fastapi>=0.111; extra == "server"
|
|
34
|
+
Requires-Dist: httpx>=0.27; extra == "server"
|
|
35
|
+
Requires-Dist: uvicorn>=0.30; extra == "server"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: tiktoken>=0.5; extra == "all"
|
|
38
|
+
Requires-Dist: sentence-transformers>=2.2; extra == "all"
|
|
39
|
+
Requires-Dist: openai>=1.0; extra == "all"
|
|
40
|
+
Requires-Dist: cohere>=5.0; extra == "all"
|
|
41
|
+
Requires-Dist: anthropic>=0.30; extra == "all"
|
|
42
|
+
Requires-Dist: mistralai>=1.0; extra == "all"
|
|
43
|
+
Requires-Dist: fastapi>=0.111; extra == "all"
|
|
44
|
+
Requires-Dist: httpx>=0.27; extra == "all"
|
|
45
|
+
Requires-Dist: uvicorn>=0.30; extra == "all"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
# DAGC — Decision-Anchored Graph Compression
|
|
49
|
+
|
|
50
|
+
`dagc` shortens agent and chat histories while prioritising the values that
|
|
51
|
+
later decisions depend on: tool arguments, IDs, paths, metrics, and confirmed
|
|
52
|
+
choices. It is designed for applications that need to reduce context size
|
|
53
|
+
without losing the evidence behind an earlier decision.
|
|
54
|
+
|
|
55
|
+
The core package is local-only. It does not make LLM or network calls unless
|
|
56
|
+
you configure an optional embedding or evaluation adapter that does.
|
|
57
|
+
|
|
58
|
+
## What it includes
|
|
59
|
+
|
|
60
|
+
- Decision-aware message compression with configurable token reduction.
|
|
61
|
+
- Zero-dependency fallback tokenizer and embedder for local use.
|
|
62
|
+
- Optional adapters for `tiktoken`, Sentence Transformers, OpenAI, and Cohere.
|
|
63
|
+
- `compress_any()` for normalising and restoring common message formats.
|
|
64
|
+
- `dagc_eval`, an optional reproducibility and benchmark toolkit.
|
|
65
|
+
- `dagc-server`, an optional HTTP proxy for compressing message arrays before
|
|
66
|
+
forwarding a request to an upstream LLM API.
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
Install the package from this repository while developing:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
After publishing, install it from PyPI:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install dagc
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The core package only requires NumPy and SciPy. For model-aligned token counts
|
|
83
|
+
and semantic embeddings, install the relevant extras:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install "dagc[tiktoken,sentence-transformers]"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Quick start
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from dagc import compress
|
|
93
|
+
|
|
94
|
+
compressed = compress(messages, target_reduction=0.85)
|
|
95
|
+
response = client.chat.completions.create(
|
|
96
|
+
model="gpt-4.1",
|
|
97
|
+
messages=compressed,
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`compress()` accepts a list of message dictionaries and returns a new list.
|
|
102
|
+
The returned messages include `_orig_idx`, which records their position in the
|
|
103
|
+
original trace.
|
|
104
|
+
|
|
105
|
+
For arbitrary message shapes, use `compress_any()` instead. It normalises
|
|
106
|
+
common message and envelope formats, compresses them, then restores the
|
|
107
|
+
original structure by default:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from dagc import compress_any
|
|
111
|
+
|
|
112
|
+
compressed_payload = compress_any(request_payload, target_reduction=0.85)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Configure production adapters
|
|
116
|
+
|
|
117
|
+
The default adapters are useful for getting started, but a tokenizer and
|
|
118
|
+
embedding model matched to your application usually produce better results.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
import dagc
|
|
122
|
+
from dagc.adapters import SentenceTransformerEmbedder, TiktokenTokenizer
|
|
123
|
+
|
|
124
|
+
dagc.configure(
|
|
125
|
+
tokenizer=TiktokenTokenizer("cl100k_base"),
|
|
126
|
+
embedder=SentenceTransformerEmbedder("all-MiniLM-L6-v2"),
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
See [examples/byok_openai_embeddings.py](examples/byok_openai_embeddings.py)
|
|
131
|
+
for a Mistral embedding example and [examples/langgraph_style_node.py](examples/langgraph_style_node.py)
|
|
132
|
+
for a workflow-node example.
|
|
133
|
+
|
|
134
|
+
## Tuning
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from dagc import DAGCConfig, compress
|
|
138
|
+
|
|
139
|
+
cfg = DAGCConfig(TARGET_REDUCTION=0.90, KEEP_LAST_K=3)
|
|
140
|
+
compressed = compress(messages, cfg=cfg)
|
|
141
|
+
|
|
142
|
+
# Equivalent inline overrides:
|
|
143
|
+
compressed = compress(messages, TARGET_REDUCTION=0.90, KEEP_LAST_K=3)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
| Field | Default | Purpose |
|
|
147
|
+
| --- | ---: | --- |
|
|
148
|
+
| `TARGET_REDUCTION` | `0.87` | Requested fraction of tokens to remove. |
|
|
149
|
+
| `KEEP_LAST_K` | `1` | Retains the latest messages as protected context. |
|
|
150
|
+
| `PROTECT_TOOL_CALLS` | `True` | Protects tool-call messages during selection. |
|
|
151
|
+
| `PROTECT_JUDGMENTS` | `True` | Protects assistant judgments and confirmations. |
|
|
152
|
+
| `USE_CAUSAL_SKELETON` | `True` | Uses the causal message graph during selection. |
|
|
153
|
+
|
|
154
|
+
Protected messages may still have their content shortened when
|
|
155
|
+
`COMPRESS_PROTECTED=True` (the default). Use the settings in `DAGCConfig` to
|
|
156
|
+
adjust those caps for your application.
|
|
157
|
+
|
|
158
|
+
## How compression works
|
|
159
|
+
|
|
160
|
+
1. The extractor identifies tool calls, decisions, and confirmations, then
|
|
161
|
+
collects the IDs, paths, values, and rationale associated with them.
|
|
162
|
+
2. The first selection phase reserves budget for decision-critical evidence.
|
|
163
|
+
3. The remaining budget is filled with causally relevant, task-relevant
|
|
164
|
+
sentences while reducing redundant context.
|
|
165
|
+
4. A final pass checks for missing critical values and adds available evidence
|
|
166
|
+
back when needed.
|
|
167
|
+
|
|
168
|
+
This is heuristic software, so evaluate it with your own traces before relying
|
|
169
|
+
on a specific reduction target in production.
|
|
170
|
+
|
|
171
|
+
## Evaluate a trace
|
|
172
|
+
|
|
173
|
+
`dagc_eval` measures whether a compressed trace still has enough information
|
|
174
|
+
to reproduce the decisions in the original trace.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from dagc_eval import TASKS, compute_drr, generate_trace
|
|
178
|
+
|
|
179
|
+
trace = generate_trace(TASKS[0])
|
|
180
|
+
result = compute_drr(trace)
|
|
181
|
+
print(result["DRR_soft"], result["RCI"])
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The default evaluation path is deterministic and runs offline. You can also
|
|
185
|
+
pass a BYOK client for reconstruction attempts that need an LLM:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
import openai
|
|
189
|
+
from dagc_eval import compute_drr
|
|
190
|
+
from dagc_eval.interfaces import OpenAIChatClient
|
|
191
|
+
|
|
192
|
+
llm = OpenAIChatClient(openai.OpenAI())
|
|
193
|
+
result = compute_drr(trace, llm=llm)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The command-line interface exposes the same workflows:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
dagc compress trace.json --target-reduction 0.85 -o compressed.json
|
|
200
|
+
dagc evaluate trace.json -o report.md
|
|
201
|
+
dagc benchmark --n-traces 3 -o benchmark.json
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Optional proxy
|
|
205
|
+
|
|
206
|
+
Install the server extra to run the wire-compatible compression proxy:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pip install "dagc[server]"
|
|
210
|
+
export UPSTREAM_BASE_URL="https://your-llm-provider.example"
|
|
211
|
+
dagc-server
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The proxy looks for a `messages`, `trace`, `conversation`, or `turns` array in
|
|
215
|
+
the request body. If compression fails, it forwards the original request
|
|
216
|
+
unchanged.
|
|
217
|
+
|
|
218
|
+
## Project layout
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
src/dagc/ Core compression, formats, adapters, and graph utilities
|
|
222
|
+
src/dagc_eval/ Evaluation, benchmarks, diagnostics, exports, and proxy server
|
|
223
|
+
examples/ Runnable integration examples
|
|
224
|
+
tests/ Unit and format-robustness tests
|
|
225
|
+
```
|
dagc-0.1.0/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# DAGC — Decision-Anchored Graph Compression
|
|
2
|
+
|
|
3
|
+
`dagc` shortens agent and chat histories while prioritising the values that
|
|
4
|
+
later decisions depend on: tool arguments, IDs, paths, metrics, and confirmed
|
|
5
|
+
choices. It is designed for applications that need to reduce context size
|
|
6
|
+
without losing the evidence behind an earlier decision.
|
|
7
|
+
|
|
8
|
+
The core package is local-only. It does not make LLM or network calls unless
|
|
9
|
+
you configure an optional embedding or evaluation adapter that does.
|
|
10
|
+
|
|
11
|
+
## What it includes
|
|
12
|
+
|
|
13
|
+
- Decision-aware message compression with configurable token reduction.
|
|
14
|
+
- Zero-dependency fallback tokenizer and embedder for local use.
|
|
15
|
+
- Optional adapters for `tiktoken`, Sentence Transformers, OpenAI, and Cohere.
|
|
16
|
+
- `compress_any()` for normalising and restoring common message formats.
|
|
17
|
+
- `dagc_eval`, an optional reproducibility and benchmark toolkit.
|
|
18
|
+
- `dagc-server`, an optional HTTP proxy for compressing message arrays before
|
|
19
|
+
forwarding a request to an upstream LLM API.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
Install the package from this repository while developing:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install -e .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
After publishing, install it from PyPI:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install dagc
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The core package only requires NumPy and SciPy. For model-aligned token counts
|
|
36
|
+
and semantic embeddings, install the relevant extras:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install "dagc[tiktoken,sentence-transformers]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from dagc import compress
|
|
46
|
+
|
|
47
|
+
compressed = compress(messages, target_reduction=0.85)
|
|
48
|
+
response = client.chat.completions.create(
|
|
49
|
+
model="gpt-4.1",
|
|
50
|
+
messages=compressed,
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`compress()` accepts a list of message dictionaries and returns a new list.
|
|
55
|
+
The returned messages include `_orig_idx`, which records their position in the
|
|
56
|
+
original trace.
|
|
57
|
+
|
|
58
|
+
For arbitrary message shapes, use `compress_any()` instead. It normalises
|
|
59
|
+
common message and envelope formats, compresses them, then restores the
|
|
60
|
+
original structure by default:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from dagc import compress_any
|
|
64
|
+
|
|
65
|
+
compressed_payload = compress_any(request_payload, target_reduction=0.85)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Configure production adapters
|
|
69
|
+
|
|
70
|
+
The default adapters are useful for getting started, but a tokenizer and
|
|
71
|
+
embedding model matched to your application usually produce better results.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import dagc
|
|
75
|
+
from dagc.adapters import SentenceTransformerEmbedder, TiktokenTokenizer
|
|
76
|
+
|
|
77
|
+
dagc.configure(
|
|
78
|
+
tokenizer=TiktokenTokenizer("cl100k_base"),
|
|
79
|
+
embedder=SentenceTransformerEmbedder("all-MiniLM-L6-v2"),
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
See [examples/byok_openai_embeddings.py](examples/byok_openai_embeddings.py)
|
|
84
|
+
for a Mistral embedding example and [examples/langgraph_style_node.py](examples/langgraph_style_node.py)
|
|
85
|
+
for a workflow-node example.
|
|
86
|
+
|
|
87
|
+
## Tuning
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from dagc import DAGCConfig, compress
|
|
91
|
+
|
|
92
|
+
cfg = DAGCConfig(TARGET_REDUCTION=0.90, KEEP_LAST_K=3)
|
|
93
|
+
compressed = compress(messages, cfg=cfg)
|
|
94
|
+
|
|
95
|
+
# Equivalent inline overrides:
|
|
96
|
+
compressed = compress(messages, TARGET_REDUCTION=0.90, KEEP_LAST_K=3)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
| Field | Default | Purpose |
|
|
100
|
+
| --- | ---: | --- |
|
|
101
|
+
| `TARGET_REDUCTION` | `0.87` | Requested fraction of tokens to remove. |
|
|
102
|
+
| `KEEP_LAST_K` | `1` | Retains the latest messages as protected context. |
|
|
103
|
+
| `PROTECT_TOOL_CALLS` | `True` | Protects tool-call messages during selection. |
|
|
104
|
+
| `PROTECT_JUDGMENTS` | `True` | Protects assistant judgments and confirmations. |
|
|
105
|
+
| `USE_CAUSAL_SKELETON` | `True` | Uses the causal message graph during selection. |
|
|
106
|
+
|
|
107
|
+
Protected messages may still have their content shortened when
|
|
108
|
+
`COMPRESS_PROTECTED=True` (the default). Use the settings in `DAGCConfig` to
|
|
109
|
+
adjust those caps for your application.
|
|
110
|
+
|
|
111
|
+
## How compression works
|
|
112
|
+
|
|
113
|
+
1. The extractor identifies tool calls, decisions, and confirmations, then
|
|
114
|
+
collects the IDs, paths, values, and rationale associated with them.
|
|
115
|
+
2. The first selection phase reserves budget for decision-critical evidence.
|
|
116
|
+
3. The remaining budget is filled with causally relevant, task-relevant
|
|
117
|
+
sentences while reducing redundant context.
|
|
118
|
+
4. A final pass checks for missing critical values and adds available evidence
|
|
119
|
+
back when needed.
|
|
120
|
+
|
|
121
|
+
This is heuristic software, so evaluate it with your own traces before relying
|
|
122
|
+
on a specific reduction target in production.
|
|
123
|
+
|
|
124
|
+
## Evaluate a trace
|
|
125
|
+
|
|
126
|
+
`dagc_eval` measures whether a compressed trace still has enough information
|
|
127
|
+
to reproduce the decisions in the original trace.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from dagc_eval import TASKS, compute_drr, generate_trace
|
|
131
|
+
|
|
132
|
+
trace = generate_trace(TASKS[0])
|
|
133
|
+
result = compute_drr(trace)
|
|
134
|
+
print(result["DRR_soft"], result["RCI"])
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The default evaluation path is deterministic and runs offline. You can also
|
|
138
|
+
pass a BYOK client for reconstruction attempts that need an LLM:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import openai
|
|
142
|
+
from dagc_eval import compute_drr
|
|
143
|
+
from dagc_eval.interfaces import OpenAIChatClient
|
|
144
|
+
|
|
145
|
+
llm = OpenAIChatClient(openai.OpenAI())
|
|
146
|
+
result = compute_drr(trace, llm=llm)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The command-line interface exposes the same workflows:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
dagc compress trace.json --target-reduction 0.85 -o compressed.json
|
|
153
|
+
dagc evaluate trace.json -o report.md
|
|
154
|
+
dagc benchmark --n-traces 3 -o benchmark.json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Optional proxy
|
|
158
|
+
|
|
159
|
+
Install the server extra to run the wire-compatible compression proxy:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pip install "dagc[server]"
|
|
163
|
+
export UPSTREAM_BASE_URL="https://your-llm-provider.example"
|
|
164
|
+
dagc-server
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The proxy looks for a `messages`, `trace`, `conversation`, or `turns` array in
|
|
168
|
+
the request body. If compression fails, it forwards the original request
|
|
169
|
+
unchanged.
|
|
170
|
+
|
|
171
|
+
## Project layout
|
|
172
|
+
|
|
173
|
+
```text
|
|
174
|
+
src/dagc/ Core compression, formats, adapters, and graph utilities
|
|
175
|
+
src/dagc_eval/ Evaluation, benchmarks, diagnostics, exports, and proxy server
|
|
176
|
+
examples/ Runnable integration examples
|
|
177
|
+
tests/ Unit and format-robustness tests
|
|
178
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dagc"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Decision-Anchored Graph Compression for agent/chat traces: preserve decision-critical artifacts with regex, embeddings, and graph algorithms, support varied message formats, and include optional evaluation tools."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
authors = [{ name = "Shaurya Sawant" }]
|
|
12
|
+
license = "MIT"
|
|
13
|
+
license-files = ["LICENSE"]
|
|
14
|
+
keywords = ["agent", "chat", "compression", "context", "llm", "memory"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"numpy>=1.22",
|
|
23
|
+
"scipy>=1.10",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/Shauryasawant/DAGC-Decision-Anchored-Graph-Compression"
|
|
28
|
+
Repository = "https://github.com/Shauryasawant/DAGC-Decision-Anchored-Graph-Compression"
|
|
29
|
+
Issues = "https://github.com/Shauryasawant/DAGC-Decision-Anchored-Graph-Compression/issues"
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
# Optional tokenizer and embedding adapters.
|
|
33
|
+
tiktoken = ["tiktoken>=0.5"]
|
|
34
|
+
sentence-transformers = ["sentence-transformers>=2.2"]
|
|
35
|
+
openai = ["openai>=1.0"]
|
|
36
|
+
cohere = ["cohere>=5.0"]
|
|
37
|
+
|
|
38
|
+
# Optional SDKs for dagc_eval adapters.
|
|
39
|
+
anthropic = ["anthropic>=0.30"]
|
|
40
|
+
mistralai = ["mistralai>=1.0"]
|
|
41
|
+
|
|
42
|
+
# Optional dependencies for dagc-server.
|
|
43
|
+
server = ["fastapi>=0.111", "httpx>=0.27", "uvicorn>=0.30"]
|
|
44
|
+
|
|
45
|
+
# All optional dependencies.
|
|
46
|
+
all = ["tiktoken>=0.5", "sentence-transformers>=2.2", "openai>=1.0", "cohere>=5.0", "anthropic>=0.30", "mistralai>=1.0", "fastapi>=0.111", "httpx>=0.27", "uvicorn>=0.30"]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
50
|
+
|
|
51
|
+
[project.scripts]
|
|
52
|
+
dagc = "dagc.cli:main"
|
|
53
|
+
dagc-server = "dagc_eval.server:run"
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
pythonpath = ["src"]
|
dagc-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
dagc — Decision-Anchored Graph Compression.
|
|
3
|
+
|
|
4
|
+
Compress long agent/chat message histories while guaranteeing that every
|
|
5
|
+
artifact a decision depends on (tool-call arguments, confirmed IDs, cited
|
|
6
|
+
metrics) survives compression. Zero required dependencies, zero LLM calls.
|
|
7
|
+
|
|
8
|
+
from dagc import compress
|
|
9
|
+
compressed = compress(messages, target_reduction=0.85)
|
|
10
|
+
response = client.chat.completions.create(model="gpt-4", messages=compressed)
|
|
11
|
+
|
|
12
|
+
Bring your own tokenizer/embedder for production-grade quality:
|
|
13
|
+
|
|
14
|
+
import dagc
|
|
15
|
+
from dagc.adapters import TiktokenTokenizer, SentenceTransformerEmbedder
|
|
16
|
+
dagc.configure(
|
|
17
|
+
tokenizer=TiktokenTokenizer("cl100k_base"),
|
|
18
|
+
embedder=SentenceTransformerEmbedder("all-MiniLM-L6-v2"),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
from .compressor import compress, compress_any, compress_dagc, DAGCConfig, DAGC_CFG
|
|
23
|
+
from .extraction import extract_decisions
|
|
24
|
+
from .graph import (
|
|
25
|
+
build_dependency_graph, attach_dependencies, compute_rci, compute_chain_rci,
|
|
26
|
+
CausalMessageGraph, CausalGraphConfig, SpectralCompressor,
|
|
27
|
+
)
|
|
28
|
+
from .config import configure, Runtime, Tokenizer, Embedder, runtime
|
|
29
|
+
from .formats import (
|
|
30
|
+
normalize_message, normalize_trace,
|
|
31
|
+
denormalize_message, denormalize_trace,
|
|
32
|
+
register_adapter as register_format_adapter,
|
|
33
|
+
)
|
|
34
|
+
from .schema import register_adapter, to_dagc_format
|
|
35
|
+
|
|
36
|
+
__version__ = "0.1.0"
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"compress", "compress_any", "compress_dagc", "DAGCConfig", "DAGC_CFG",
|
|
40
|
+
"extract_decisions",
|
|
41
|
+
"build_dependency_graph", "attach_dependencies", "compute_rci", "compute_chain_rci",
|
|
42
|
+
"CausalMessageGraph", "CausalGraphConfig", "SpectralCompressor",
|
|
43
|
+
"configure", "Runtime", "Tokenizer", "Embedder", "runtime",
|
|
44
|
+
"normalize_message", "normalize_trace",
|
|
45
|
+
"denormalize_message", "denormalize_trace",
|
|
46
|
+
"register_adapter", "register_format_adapter", "to_dagc_format",
|
|
47
|
+
]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Optional, real-quality Tokenizer/Embedder adapters. Each is lazily
|
|
3
|
+
imported so that importing `dagc.adapters` never requires having every
|
|
4
|
+
vendor SDK installed -- only the one you actually instantiate.
|
|
5
|
+
|
|
6
|
+
Install what you need:
|
|
7
|
+
pip install dagc[tiktoken]
|
|
8
|
+
pip install dagc[sentence-transformers]
|
|
9
|
+
pip install dagc[openai]
|
|
10
|
+
|
|
11
|
+
Or write your own -- any object with .encode()/.decode() (Tokenizer) or
|
|
12
|
+
.encode() -> np.ndarray (Embedder) works, see dagc.config.Tokenizer /
|
|
13
|
+
dagc.config.Embedder for the exact protocol.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
from typing import List
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TiktokenTokenizer:
|
|
21
|
+
"""Real BPE token counts via OpenAI's tiktoken. pip install tiktoken."""
|
|
22
|
+
def __init__(self, encoding_name: str = "cl100k_base"):
|
|
23
|
+
try:
|
|
24
|
+
import tiktoken
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
raise ImportError(
|
|
27
|
+
"TiktokenTokenizer needs tiktoken. Install it with "
|
|
28
|
+
"pip install -e '.[tiktoken]'"
|
|
29
|
+
) from e
|
|
30
|
+
self._enc = tiktoken.get_encoding(encoding_name)
|
|
31
|
+
|
|
32
|
+
def encode(self, text: str) -> List[int]:
|
|
33
|
+
return self._enc.encode(str(text))
|
|
34
|
+
|
|
35
|
+
def decode(self, tokens: List[int]) -> str:
|
|
36
|
+
return self._enc.decode(tokens)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SentenceTransformerEmbedder:
|
|
40
|
+
"""Local, free, good-quality embeddings. pip install sentence-transformers."""
|
|
41
|
+
def __init__(self, model_name: str = "all-MiniLM-L6-v2"):
|
|
42
|
+
try:
|
|
43
|
+
from sentence_transformers import SentenceTransformer
|
|
44
|
+
except ImportError as e:
|
|
45
|
+
raise ImportError(
|
|
46
|
+
"SentenceTransformerEmbedder needs sentence-transformers. "
|
|
47
|
+
"Install it with pip install -e '.[sentence-transformers]'"
|
|
48
|
+
) from e
|
|
49
|
+
self._model = SentenceTransformer(model_name)
|
|
50
|
+
|
|
51
|
+
def encode(self, texts: List[str], normalize_embeddings: bool = False) -> np.ndarray:
|
|
52
|
+
return np.asarray(
|
|
53
|
+
self._model.encode(list(texts), normalize_embeddings=normalize_embeddings)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class OpenAIEmbedder:
|
|
58
|
+
"""
|
|
59
|
+
BYOK wrapper around any OpenAI-compatible embeddings endpoint.
|
|
60
|
+
Pass your own already-configured client (openai.OpenAI(...)) --
|
|
61
|
+
dagc never sees or stores your API key.
|
|
62
|
+
"""
|
|
63
|
+
def __init__(self, client, model: str = "text-embedding-3-small"):
|
|
64
|
+
self._client = client
|
|
65
|
+
self._model = model
|
|
66
|
+
|
|
67
|
+
def encode(self, texts: List[str], normalize_embeddings: bool = False) -> np.ndarray:
|
|
68
|
+
resp = self._client.embeddings.create(model=self._model, input=list(texts))
|
|
69
|
+
vecs = np.array([d.embedding for d in resp.data], dtype=np.float32)
|
|
70
|
+
if normalize_embeddings:
|
|
71
|
+
norms = np.linalg.norm(vecs, axis=1, keepdims=True)
|
|
72
|
+
norms[norms == 0] = 1.0
|
|
73
|
+
vecs = vecs / norms
|
|
74
|
+
return vecs
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class CohereEmbedder:
|
|
78
|
+
"""BYOK wrapper around a Cohere client (cohere.Client(...))."""
|
|
79
|
+
def __init__(self, client, model: str = "embed-english-v3.0",
|
|
80
|
+
input_type: str = "search_document"):
|
|
81
|
+
self._client = client
|
|
82
|
+
self._model = model
|
|
83
|
+
self._input_type = input_type
|
|
84
|
+
|
|
85
|
+
def encode(self, texts: List[str], normalize_embeddings: bool = False) -> np.ndarray:
|
|
86
|
+
resp = self._client.embed(texts=list(texts), model=self._model,
|
|
87
|
+
input_type=self._input_type)
|
|
88
|
+
vecs = np.array(resp.embeddings, dtype=np.float32)
|
|
89
|
+
if normalize_embeddings:
|
|
90
|
+
norms = np.linalg.norm(vecs, axis=1, keepdims=True)
|
|
91
|
+
norms[norms == 0] = 1.0
|
|
92
|
+
vecs = vecs / norms
|
|
93
|
+
return vecs
|