deepcq 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.
deepcq-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ternary-ai
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.
deepcq-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: deepcq
3
+ Version: 0.1.0
4
+ Summary: Deep Context Query — intent-aware memory querying for LLM agents
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/ternary-ai/deepcq
7
+ Project-URL: Repository, https://github.com/ternary-ai/deepcq
8
+ Project-URL: Issues, https://github.com/ternary-ai/deepcq/issues
9
+ Keywords: llm,agents,memory,context,rag,prompt-engineering
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=7.0; extra == "dev"
18
+ Dynamic: license-file
19
+
20
+ # deepcq
21
+
22
+ **Deep Context Query** — intent-aware memory querying for LLM agents.
23
+
24
+ deepcq sits between an agent's memory and its prompt. Instead of stuffing an
25
+ entire memory blob into every LLM call, it scores which sections of memory
26
+ are actually relevant to the current query and returns only those — cutting
27
+ prompt size while keeping recall.
28
+
29
+ No embeddings, no vector store, no external dependencies. Relevance is
30
+ computed from the query's own tokens against text already in memory, using
31
+ plain regex and a lightweight stemmer.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install deepcq
37
+ ```
38
+
39
+ For local development from a clone:
40
+
41
+ ```bash
42
+ cd deepcq
43
+ pip install -e ".[dev]"
44
+ ```
45
+
46
+ ## Core idea
47
+
48
+ You declare a `MemorySchema`: a list of section names such as
49
+ `["status", "errors", "metrics"]`. deepcq doesn't need to know what those
50
+ sections mean — it derives what's relevant from the words in each query
51
+ itself ("query-term-first"), rather than from a hand-built keyword table.
52
+
53
+ ```python
54
+ from deepcq import DeepCQ, MemorySchema
55
+
56
+ schema = MemorySchema(sections=["status", "errors", "metrics"])
57
+ cq = DeepCQ(schema=schema)
58
+
59
+ cq.state.set("errors", "[errors]\n- OOM at 02:15\n")
60
+ cq.state.set("metrics", "[metrics]\nCPU: 78%\nMemory: 4.2GB\n")
61
+ cq.state.set("status", "[status]\nAll services running\n")
62
+
63
+ result = cq.query("any memory or cpu spikes?")
64
+ print(result.content) # only the metrics section
65
+ print(result.sections_included) # ["metrics"]
66
+ print(f"{result.metrics.reduction_pct:.1f}% smaller")
67
+ ```
68
+
69
+ ## How it works
70
+
71
+ ### 1. Five memory tiers
72
+
73
+ `DeepCQ` manages five tiers, each suited to a different memory lifetime:
74
+
75
+ | Tier | Class | Lifetime | Purpose |
76
+ |------|-------|----------|---------|
77
+ | T1 | `AppState` | live | Current state of the running application (sections keyed by name) |
78
+ | T2 | `SessionCache` | per-session, bounded | Ephemeral key/value scratch space, deduped, LRU-evicted |
79
+ | T3 | `History` | sliding window | Last N conversation turns |
80
+ | T4 | `LongTermStore` | persistent | Append-only notes, optionally file-backed (JSON) |
81
+ | T5 | `ReferenceData` | persistent, lazy | Static config/reference data, optionally file-backed |
82
+
83
+ Each tier renders itself to text via `as_text()`. `DeepCQ.query()` pulls text
84
+ from all five tiers before scoring.
85
+
86
+ ### 2. Tokenizing the query (`tokens.py`)
87
+
88
+ The query is lowercased, split into words, stripped of stopwords, and each
89
+ surviving token is paired with a suffix-stripped stem (`spikes` → `spike`,
90
+ `running` → `run`). No external NLP dependency — it's a small suffix-rule
91
+ table plus a doubled-consonant cleanup (`running` → `runn` → `run`).
92
+
93
+ ### 3. Scoring sections (`intent.py`)
94
+
95
+ For each section in the schema, deepcq builds a vocabulary from:
96
+ 1. The section name itself (tokenized/stemmed).
97
+ 2. Optional `keywords` you declare for that section in the schema (useful
98
+ when a section name is jargon, e.g. `"db"` → `["database", "postgres"]`).
99
+ 3. Whatever text is currently stored under that section's header across all
100
+ tiers.
101
+
102
+ The section's score is the fraction of unique query tokens found in that
103
+ vocabulary. Sections scoring at or above `schema.confidence_threshold`
104
+ (default `0.3`) are "included".
105
+
106
+ ### 4. Extracting content (`schema.py`, `regex_gen.py`)
107
+
108
+ Sections are delimited in stored text by a header, `"[{name}]"` by default
109
+ (configurable via `section_header_format`, e.g. `"## {name}"`). Each section
110
+ compiles to a regex that captures from its header up to the next header or
111
+ end of string — this is how `MemorySchema.extraction_pattern(section)` pulls
112
+ a section's block out of raw tier text.
113
+
114
+ For the two free-text tiers (`cache`, `history`, which aren't
115
+ section-delimited), deepcq instead builds a single regex out of all query
116
+ tokens and includes that tier's text only if any token matches
117
+ (`build_content_pattern` in `regex_gen.py`).
118
+
119
+ ### 5. Result (`result.py`)
120
+
121
+ `query()` returns a `QueryResult`:
122
+
123
+ ```python
124
+ @dataclass
125
+ class QueryResult:
126
+ content: str # the filtered, concatenated text
127
+ intent: dict[str, float] # per-section relevance scores
128
+ sections_included: list[str]
129
+ sections_excluded: list[str]
130
+ content_pattern: str # the regex used against cache/history
131
+ metrics: OptimizationMetrics # original_chars, filtered_chars, reduction_pct
132
+ ```
133
+
134
+ ### Fallback safety
135
+
136
+ If no section clears the confidence threshold and
137
+ `schema.fallback_include_all` is `True` (the default), `query()` returns the
138
+ full unfiltered text instead of nothing — this prevents a vague query from
139
+ silently discarding memory the agent might actually need.
140
+
141
+ ## Package layout
142
+
143
+ ```
144
+ deepcq/
145
+ ├── pyproject.toml
146
+ ├── README.md
147
+ ├── deepcq/ # the installable package
148
+ │ ├── __init__.py # public API: DeepCQ, MemorySchema, QueryResult, ...
149
+ │ ├── core.py # DeepCQ orchestrator (query())
150
+ │ ├── schema.py # MemorySchema + extraction regex compilation
151
+ │ ├── intent.py # score_sections()
152
+ │ ├── tokens.py # tokenize() / stem()
153
+ │ ├── regex_gen.py # query-token regex builder for free-text tiers
154
+ │ ├── result.py # QueryResult / OptimizationMetrics dataclasses
155
+ │ ├── memory/
156
+ │ │ ├── state.py # T1 AppState
157
+ │ │ ├── session.py # T2 SessionCache, T3 History
158
+ │ │ └── store.py # T4 LongTermStore, T5 ReferenceData
159
+ │ └── integrations/
160
+ │ └── ternary/ # optional adapter for ternary-ai agents/prompts
161
+ │ ├── adapters.py # ToolContextProvider, LangChainMemoryAdapter
162
+ │ └── prompt_library.py # PromptLibrary (loads ternary-ai/prompts CSVs)
163
+ ├── examples/
164
+ │ ├── openai_agents_sdk.py
165
+ │ ├── claude_agents_sdk.py
166
+ │ ├── qwen_agents_sdk.py
167
+ │ └── google_a2a.py
168
+ └── tests/
169
+ ```
170
+
171
+ ## Optional integrations
172
+
173
+ `deepcq.integrations.ternary` is an optional adapter layer for using deepcq
174
+ as a memory/context backend in agent frameworks. It has no hard dependency
175
+ on any framework — both adapters duck-type the interface they mimic:
176
+
177
+ - **`ToolContextProvider`** — formats a `QueryResult` as a plain string to
178
+ inject into a system prompt or tool call.
179
+ - **`LangChainMemoryAdapter`** — duck-types LangChain's `BaseChatMemory`
180
+ (`save_context` / `load_memory_variables` / `clear`), so it can be passed
181
+ directly as a chain's `memory=` argument without installing LangChain.
182
+ - **`PromptLibrary`** — loads the CSV prompt templates from the
183
+ [ternary-ai/prompts](https://github.com/ternary-ai/prompts) repo (locally
184
+ or via `urllib` from raw GitHub URLs) into a deepcq schema, so prompt
185
+ templates themselves can be retrieved by intent.
186
+
187
+ ```python
188
+ from deepcq import DeepCQ, MemorySchema
189
+ from deepcq.integrations.ternary import ToolContextProvider, PromptLibrary
190
+
191
+ cq = DeepCQ(schema=MemorySchema(sections=["research", "risk-management"]))
192
+ provider = ToolContextProvider(cq)
193
+ context = provider.get_context("what is my portfolio concentration risk?")
194
+ ```
195
+
196
+ See `examples/openai_agents_sdk.py`, `examples/claude_agents_sdk.py`,
197
+ `examples/qwen_agents_sdk.py`, and `examples/google_a2a.py` for focused
198
+ integration examples. They are designed as SDK wiring references, so you
199
+ may need to adjust the exact import path to match the SDK version you have
200
+ installed.
201
+
202
+ ## Testing
203
+
204
+ ```bash
205
+ pip install -e ".[dev]"
206
+ pytest
207
+ ```
208
+
209
+ ## Moving this into its own repository
210
+
211
+ This directory is self-contained — nothing outside `deepcq/` is required.
212
+ To split it out:
213
+
214
+ ```bash
215
+ cp -r deepcq/ /path/to/new/deepcq-repo
216
+ cd /path/to/new/deepcq-repo
217
+ git init
218
+ pip install -e ".[dev]"
219
+ pytest
220
+ ```
deepcq-0.1.0/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # deepcq
2
+
3
+ **Deep Context Query** — intent-aware memory querying for LLM agents.
4
+
5
+ deepcq sits between an agent's memory and its prompt. Instead of stuffing an
6
+ entire memory blob into every LLM call, it scores which sections of memory
7
+ are actually relevant to the current query and returns only those — cutting
8
+ prompt size while keeping recall.
9
+
10
+ No embeddings, no vector store, no external dependencies. Relevance is
11
+ computed from the query's own tokens against text already in memory, using
12
+ plain regex and a lightweight stemmer.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install deepcq
18
+ ```
19
+
20
+ For local development from a clone:
21
+
22
+ ```bash
23
+ cd deepcq
24
+ pip install -e ".[dev]"
25
+ ```
26
+
27
+ ## Core idea
28
+
29
+ You declare a `MemorySchema`: a list of section names such as
30
+ `["status", "errors", "metrics"]`. deepcq doesn't need to know what those
31
+ sections mean — it derives what's relevant from the words in each query
32
+ itself ("query-term-first"), rather than from a hand-built keyword table.
33
+
34
+ ```python
35
+ from deepcq import DeepCQ, MemorySchema
36
+
37
+ schema = MemorySchema(sections=["status", "errors", "metrics"])
38
+ cq = DeepCQ(schema=schema)
39
+
40
+ cq.state.set("errors", "[errors]\n- OOM at 02:15\n")
41
+ cq.state.set("metrics", "[metrics]\nCPU: 78%\nMemory: 4.2GB\n")
42
+ cq.state.set("status", "[status]\nAll services running\n")
43
+
44
+ result = cq.query("any memory or cpu spikes?")
45
+ print(result.content) # only the metrics section
46
+ print(result.sections_included) # ["metrics"]
47
+ print(f"{result.metrics.reduction_pct:.1f}% smaller")
48
+ ```
49
+
50
+ ## How it works
51
+
52
+ ### 1. Five memory tiers
53
+
54
+ `DeepCQ` manages five tiers, each suited to a different memory lifetime:
55
+
56
+ | Tier | Class | Lifetime | Purpose |
57
+ |------|-------|----------|---------|
58
+ | T1 | `AppState` | live | Current state of the running application (sections keyed by name) |
59
+ | T2 | `SessionCache` | per-session, bounded | Ephemeral key/value scratch space, deduped, LRU-evicted |
60
+ | T3 | `History` | sliding window | Last N conversation turns |
61
+ | T4 | `LongTermStore` | persistent | Append-only notes, optionally file-backed (JSON) |
62
+ | T5 | `ReferenceData` | persistent, lazy | Static config/reference data, optionally file-backed |
63
+
64
+ Each tier renders itself to text via `as_text()`. `DeepCQ.query()` pulls text
65
+ from all five tiers before scoring.
66
+
67
+ ### 2. Tokenizing the query (`tokens.py`)
68
+
69
+ The query is lowercased, split into words, stripped of stopwords, and each
70
+ surviving token is paired with a suffix-stripped stem (`spikes` → `spike`,
71
+ `running` → `run`). No external NLP dependency — it's a small suffix-rule
72
+ table plus a doubled-consonant cleanup (`running` → `runn` → `run`).
73
+
74
+ ### 3. Scoring sections (`intent.py`)
75
+
76
+ For each section in the schema, deepcq builds a vocabulary from:
77
+ 1. The section name itself (tokenized/stemmed).
78
+ 2. Optional `keywords` you declare for that section in the schema (useful
79
+ when a section name is jargon, e.g. `"db"` → `["database", "postgres"]`).
80
+ 3. Whatever text is currently stored under that section's header across all
81
+ tiers.
82
+
83
+ The section's score is the fraction of unique query tokens found in that
84
+ vocabulary. Sections scoring at or above `schema.confidence_threshold`
85
+ (default `0.3`) are "included".
86
+
87
+ ### 4. Extracting content (`schema.py`, `regex_gen.py`)
88
+
89
+ Sections are delimited in stored text by a header, `"[{name}]"` by default
90
+ (configurable via `section_header_format`, e.g. `"## {name}"`). Each section
91
+ compiles to a regex that captures from its header up to the next header or
92
+ end of string — this is how `MemorySchema.extraction_pattern(section)` pulls
93
+ a section's block out of raw tier text.
94
+
95
+ For the two free-text tiers (`cache`, `history`, which aren't
96
+ section-delimited), deepcq instead builds a single regex out of all query
97
+ tokens and includes that tier's text only if any token matches
98
+ (`build_content_pattern` in `regex_gen.py`).
99
+
100
+ ### 5. Result (`result.py`)
101
+
102
+ `query()` returns a `QueryResult`:
103
+
104
+ ```python
105
+ @dataclass
106
+ class QueryResult:
107
+ content: str # the filtered, concatenated text
108
+ intent: dict[str, float] # per-section relevance scores
109
+ sections_included: list[str]
110
+ sections_excluded: list[str]
111
+ content_pattern: str # the regex used against cache/history
112
+ metrics: OptimizationMetrics # original_chars, filtered_chars, reduction_pct
113
+ ```
114
+
115
+ ### Fallback safety
116
+
117
+ If no section clears the confidence threshold and
118
+ `schema.fallback_include_all` is `True` (the default), `query()` returns the
119
+ full unfiltered text instead of nothing — this prevents a vague query from
120
+ silently discarding memory the agent might actually need.
121
+
122
+ ## Package layout
123
+
124
+ ```
125
+ deepcq/
126
+ ├── pyproject.toml
127
+ ├── README.md
128
+ ├── deepcq/ # the installable package
129
+ │ ├── __init__.py # public API: DeepCQ, MemorySchema, QueryResult, ...
130
+ │ ├── core.py # DeepCQ orchestrator (query())
131
+ │ ├── schema.py # MemorySchema + extraction regex compilation
132
+ │ ├── intent.py # score_sections()
133
+ │ ├── tokens.py # tokenize() / stem()
134
+ │ ├── regex_gen.py # query-token regex builder for free-text tiers
135
+ │ ├── result.py # QueryResult / OptimizationMetrics dataclasses
136
+ │ ├── memory/
137
+ │ │ ├── state.py # T1 AppState
138
+ │ │ ├── session.py # T2 SessionCache, T3 History
139
+ │ │ └── store.py # T4 LongTermStore, T5 ReferenceData
140
+ │ └── integrations/
141
+ │ └── ternary/ # optional adapter for ternary-ai agents/prompts
142
+ │ ├── adapters.py # ToolContextProvider, LangChainMemoryAdapter
143
+ │ └── prompt_library.py # PromptLibrary (loads ternary-ai/prompts CSVs)
144
+ ├── examples/
145
+ │ ├── openai_agents_sdk.py
146
+ │ ├── claude_agents_sdk.py
147
+ │ ├── qwen_agents_sdk.py
148
+ │ └── google_a2a.py
149
+ └── tests/
150
+ ```
151
+
152
+ ## Optional integrations
153
+
154
+ `deepcq.integrations.ternary` is an optional adapter layer for using deepcq
155
+ as a memory/context backend in agent frameworks. It has no hard dependency
156
+ on any framework — both adapters duck-type the interface they mimic:
157
+
158
+ - **`ToolContextProvider`** — formats a `QueryResult` as a plain string to
159
+ inject into a system prompt or tool call.
160
+ - **`LangChainMemoryAdapter`** — duck-types LangChain's `BaseChatMemory`
161
+ (`save_context` / `load_memory_variables` / `clear`), so it can be passed
162
+ directly as a chain's `memory=` argument without installing LangChain.
163
+ - **`PromptLibrary`** — loads the CSV prompt templates from the
164
+ [ternary-ai/prompts](https://github.com/ternary-ai/prompts) repo (locally
165
+ or via `urllib` from raw GitHub URLs) into a deepcq schema, so prompt
166
+ templates themselves can be retrieved by intent.
167
+
168
+ ```python
169
+ from deepcq import DeepCQ, MemorySchema
170
+ from deepcq.integrations.ternary import ToolContextProvider, PromptLibrary
171
+
172
+ cq = DeepCQ(schema=MemorySchema(sections=["research", "risk-management"]))
173
+ provider = ToolContextProvider(cq)
174
+ context = provider.get_context("what is my portfolio concentration risk?")
175
+ ```
176
+
177
+ See `examples/openai_agents_sdk.py`, `examples/claude_agents_sdk.py`,
178
+ `examples/qwen_agents_sdk.py`, and `examples/google_a2a.py` for focused
179
+ integration examples. They are designed as SDK wiring references, so you
180
+ may need to adjust the exact import path to match the SDK version you have
181
+ installed.
182
+
183
+ ## Testing
184
+
185
+ ```bash
186
+ pip install -e ".[dev]"
187
+ pytest
188
+ ```
189
+
190
+ ## Moving this into its own repository
191
+
192
+ This directory is self-contained — nothing outside `deepcq/` is required.
193
+ To split it out:
194
+
195
+ ```bash
196
+ cp -r deepcq/ /path/to/new/deepcq-repo
197
+ cd /path/to/new/deepcq-repo
198
+ git init
199
+ pip install -e ".[dev]"
200
+ pytest
201
+ ```
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import import_module
4
+
5
+ __version__ = "0.1.0"
6
+
7
+ _EXPORTS = {
8
+ "DeepCQ": ("deepcq.core", "DeepCQ"),
9
+ "MemorySchema": ("deepcq.schema", "MemorySchema"),
10
+ "QueryResult": ("deepcq.result", "QueryResult"),
11
+ "OptimizationMetrics": ("deepcq.result", "OptimizationMetrics"),
12
+ "AppState": ("deepcq.memory.state", "AppState"),
13
+ "SessionCache": ("deepcq.memory.session", "SessionCache"),
14
+ "History": ("deepcq.memory.session", "History"),
15
+ "LongTermStore": ("deepcq.memory.store", "LongTermStore"),
16
+ "ReferenceData": ("deepcq.memory.store", "ReferenceData"),
17
+ }
18
+
19
+ __all__ = [
20
+ "DeepCQ", "MemorySchema", "QueryResult", "OptimizationMetrics",
21
+ "AppState", "SessionCache", "History", "LongTermStore", "ReferenceData",
22
+ ]
23
+
24
+
25
+ def __getattr__(name: str):
26
+ try:
27
+ module_name, attribute_name = _EXPORTS[name]
28
+ except KeyError as exc:
29
+ raise AttributeError(name) from None
30
+ module = import_module(module_name)
31
+ value = getattr(module, attribute_name)
32
+ globals()[name] = value
33
+ return value
34
+
35
+
36
+ def __dir__() -> list[str]:
37
+ return sorted(set(globals()) | set(__all__))
@@ -0,0 +1,126 @@
1
+ """DeepCQ — main orchestrator."""
2
+ from __future__ import annotations
3
+
4
+ from .schema import MemorySchema
5
+ from .intent import score_sections
6
+ from .regex_gen import build_content_pattern, content_pattern_string
7
+ from .result import QueryResult, OptimizationMetrics
8
+ from .memory.state import AppState
9
+ from .memory.session import SessionCache, History
10
+ from .memory.store import LongTermStore, ReferenceData
11
+
12
+
13
+ class DeepCQ:
14
+ """
15
+ Deep Context Query.
16
+
17
+ Manages five memory tiers and queries them dynamically using regex
18
+ patterns generated from the user's query. No domain knowledge is
19
+ baked in — the schema declares section names only.
20
+
21
+ Example::
22
+
23
+ from deepcq import DeepCQ, MemorySchema
24
+
25
+ schema = MemorySchema(sections=["status", "errors", "metrics"])
26
+ cq = DeepCQ(schema=schema)
27
+
28
+ cq.state.set("errors", "[errors]\\n- OOM at 02:15\\n")
29
+ cq.state.set("metrics", "[metrics]\\nCPU: 78%\\nMemory: 4.2GB\\n")
30
+ cq.state.set("status", "[status]\\nAll services running\\n")
31
+
32
+ result = cq.query("any memory or cpu spikes?")
33
+ print(result.content)
34
+ print(f"Reduction: {result.metrics.reduction_pct:.1f}%")
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ schema: MemorySchema,
40
+ store_path: str | None = None,
41
+ reference_path: str | None = None,
42
+ ) -> None:
43
+ self.schema = schema
44
+ self.state = AppState()
45
+ self.cache = SessionCache()
46
+ self.history = History()
47
+ self.notes = LongTermStore(store_path)
48
+ self.reference = ReferenceData(reference_path)
49
+
50
+ def query(self, query: str) -> QueryResult:
51
+ """
52
+ Query all memory tiers and return only content relevant to the query.
53
+ """
54
+ tier_texts: dict[str, str] = {
55
+ "state": self.state.as_text(),
56
+ "cache": self.cache.as_text(),
57
+ "history": self.history.as_text(),
58
+ "notes": self.notes.as_text(),
59
+ "reference": self.reference.as_text(),
60
+ }
61
+
62
+ full_text = "\n".join(v for v in tier_texts.values() if v)
63
+ original_chars = len(full_text)
64
+
65
+ intent = score_sections(query, self.schema, tier_texts)
66
+
67
+ included: list[str] = []
68
+ excluded: list[str] = []
69
+ for section in self.schema.sections:
70
+ if intent.get(section, 0.0) >= self.schema.confidence_threshold:
71
+ included.append(section)
72
+ else:
73
+ excluded.append(section)
74
+
75
+ if not included and self.schema.fallback_include_all:
76
+ return QueryResult(
77
+ content=full_text,
78
+ intent=intent,
79
+ sections_included=list(self.schema.sections),
80
+ sections_excluded=[],
81
+ content_pattern=content_pattern_string(query),
82
+ metrics=OptimizationMetrics(
83
+ original_chars=original_chars,
84
+ filtered_chars=original_chars,
85
+ reduction_pct=0.0,
86
+ ),
87
+ )
88
+
89
+ parts: list[str] = []
90
+
91
+ for section in included:
92
+ pat = self.schema.extraction_pattern(section)
93
+ for tier_name in ("state", "notes", "reference"):
94
+ text = tier_texts[tier_name]
95
+ if not text:
96
+ continue
97
+ m = pat.search(text)
98
+ if m:
99
+ parts.append(m.group(0).strip())
100
+
101
+ content_pat = build_content_pattern(query)
102
+ for tier_name in ("cache", "history"):
103
+ text = tier_texts[tier_name]
104
+ if text and content_pat.search(text):
105
+ parts.append(text.strip())
106
+
107
+ content = "\n\n".join(p for p in parts if p)
108
+ filtered_chars = len(content)
109
+ reduction_pct = (
110
+ round((1.0 - filtered_chars / original_chars) * 100, 1)
111
+ if original_chars > 0
112
+ else 0.0
113
+ )
114
+
115
+ return QueryResult(
116
+ content=content,
117
+ intent=intent,
118
+ sections_included=included,
119
+ sections_excluded=excluded,
120
+ content_pattern=content_pattern_string(query),
121
+ metrics=OptimizationMetrics(
122
+ original_chars=original_chars,
123
+ filtered_chars=filtered_chars,
124
+ reduction_pct=reduction_pct,
125
+ ),
126
+ )
File without changes
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import import_module
4
+
5
+ _EXPORTS = {
6
+ "PromptLibrary": ("deepcq.integrations.ternary.prompt_library", "PromptLibrary"),
7
+ "ToolContextProvider": ("deepcq.integrations.ternary.adapters", "ToolContextProvider"),
8
+ "LangChainMemoryAdapter": ("deepcq.integrations.ternary.adapters", "LangChainMemoryAdapter"),
9
+ }
10
+
11
+ __all__ = ["PromptLibrary", "ToolContextProvider", "LangChainMemoryAdapter"]
12
+
13
+
14
+ def __getattr__(name: str):
15
+ try:
16
+ module_name, attribute_name = _EXPORTS[name]
17
+ except KeyError:
18
+ raise AttributeError(name) from None
19
+ module = import_module(module_name)
20
+ value = getattr(module, attribute_name)
21
+ globals()[name] = value
22
+ return value
23
+
24
+
25
+ def __dir__() -> list[str]:
26
+ return sorted(set(globals()) | set(__all__))