tapps-agents 3.5.41__py3-none-any.whl → 3.6.1__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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/reviewer/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- tapps_agents/cli/commands/health.py +665 -665
- tapps_agents/cli/commands/top_level.py +3586 -3586
- tapps_agents/core/artifact_context_builder.py +293 -0
- tapps_agents/core/config.py +33 -0
- tapps_agents/health/orchestrator.py +271 -271
- tapps_agents/resources/__init__.py +5 -0
- tapps_agents/resources/claude/__init__.py +1 -0
- tapps_agents/resources/claude/commands/README.md +156 -0
- tapps_agents/resources/claude/commands/__init__.py +1 -0
- tapps_agents/resources/claude/commands/build-fix.md +22 -0
- tapps_agents/resources/claude/commands/build.md +77 -0
- tapps_agents/resources/claude/commands/debug.md +53 -0
- tapps_agents/resources/claude/commands/design.md +68 -0
- tapps_agents/resources/claude/commands/docs.md +53 -0
- tapps_agents/resources/claude/commands/e2e.md +22 -0
- tapps_agents/resources/claude/commands/fix.md +54 -0
- tapps_agents/resources/claude/commands/implement.md +53 -0
- tapps_agents/resources/claude/commands/improve.md +53 -0
- tapps_agents/resources/claude/commands/library-docs.md +64 -0
- tapps_agents/resources/claude/commands/lint.md +52 -0
- tapps_agents/resources/claude/commands/plan.md +65 -0
- tapps_agents/resources/claude/commands/refactor-clean.md +21 -0
- tapps_agents/resources/claude/commands/refactor.md +55 -0
- tapps_agents/resources/claude/commands/review.md +67 -0
- tapps_agents/resources/claude/commands/score.md +60 -0
- tapps_agents/resources/claude/commands/security-review.md +22 -0
- tapps_agents/resources/claude/commands/security-scan.md +54 -0
- tapps_agents/resources/claude/commands/tdd.md +24 -0
- tapps_agents/resources/claude/commands/test-coverage.md +21 -0
- tapps_agents/resources/claude/commands/test.md +54 -0
- tapps_agents/resources/claude/commands/update-codemaps.md +20 -0
- tapps_agents/resources/claude/commands/update-docs.md +21 -0
- tapps_agents/resources/claude/skills/__init__.py +1 -0
- tapps_agents/resources/claude/skills/analyst/SKILL.md +272 -0
- tapps_agents/resources/claude/skills/analyst/__init__.py +1 -0
- tapps_agents/resources/claude/skills/architect/SKILL.md +282 -0
- tapps_agents/resources/claude/skills/architect/__init__.py +1 -0
- tapps_agents/resources/claude/skills/backend-patterns/SKILL.md +30 -0
- tapps_agents/resources/claude/skills/backend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/coding-standards/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/coding-standards/__init__.py +1 -0
- tapps_agents/resources/claude/skills/debugger/SKILL.md +203 -0
- tapps_agents/resources/claude/skills/debugger/__init__.py +1 -0
- tapps_agents/resources/claude/skills/designer/SKILL.md +243 -0
- tapps_agents/resources/claude/skills/designer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/documenter/SKILL.md +252 -0
- tapps_agents/resources/claude/skills/documenter/__init__.py +1 -0
- tapps_agents/resources/claude/skills/enhancer/SKILL.md +307 -0
- tapps_agents/resources/claude/skills/enhancer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/evaluator/SKILL.md +204 -0
- tapps_agents/resources/claude/skills/evaluator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/frontend-patterns/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/frontend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/implementer/SKILL.md +188 -0
- tapps_agents/resources/claude/skills/implementer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/improver/SKILL.md +218 -0
- tapps_agents/resources/claude/skills/improver/__init__.py +1 -0
- tapps_agents/resources/claude/skills/ops/SKILL.md +281 -0
- tapps_agents/resources/claude/skills/ops/__init__.py +1 -0
- tapps_agents/resources/claude/skills/orchestrator/SKILL.md +390 -0
- tapps_agents/resources/claude/skills/orchestrator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/planner/SKILL.md +254 -0
- tapps_agents/resources/claude/skills/planner/__init__.py +1 -0
- tapps_agents/resources/claude/skills/reviewer/SKILL.md +434 -0
- tapps_agents/resources/claude/skills/reviewer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/security-review/SKILL.md +31 -0
- tapps_agents/resources/claude/skills/security-review/__init__.py +1 -0
- tapps_agents/resources/claude/skills/simple-mode/SKILL.md +695 -0
- tapps_agents/resources/claude/skills/simple-mode/__init__.py +1 -0
- tapps_agents/resources/claude/skills/tester/SKILL.md +219 -0
- tapps_agents/resources/claude/skills/tester/__init__.py +1 -0
- tapps_agents/resources/cursor/.cursorignore +35 -0
- tapps_agents/resources/cursor/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/build-fix.md +11 -0
- tapps_agents/resources/cursor/commands/build.md +11 -0
- tapps_agents/resources/cursor/commands/e2e.md +11 -0
- tapps_agents/resources/cursor/commands/fix.md +11 -0
- tapps_agents/resources/cursor/commands/refactor-clean.md +11 -0
- tapps_agents/resources/cursor/commands/review.md +11 -0
- tapps_agents/resources/cursor/commands/security-review.md +11 -0
- tapps_agents/resources/cursor/commands/tdd.md +11 -0
- tapps_agents/resources/cursor/commands/test-coverage.md +11 -0
- tapps_agents/resources/cursor/commands/test.md +11 -0
- tapps_agents/resources/cursor/commands/update-codemaps.md +10 -0
- tapps_agents/resources/cursor/commands/update-docs.md +11 -0
- tapps_agents/resources/cursor/rules/__init__.py +1 -0
- tapps_agents/resources/cursor/rules/agent-capabilities.mdc +687 -0
- tapps_agents/resources/cursor/rules/coding-style.mdc +31 -0
- tapps_agents/resources/cursor/rules/command-reference.mdc +2081 -0
- tapps_agents/resources/cursor/rules/cursor-mode-usage.mdc +125 -0
- tapps_agents/resources/cursor/rules/git-workflow.mdc +29 -0
- tapps_agents/resources/cursor/rules/performance.mdc +29 -0
- tapps_agents/resources/cursor/rules/project-context.mdc +163 -0
- tapps_agents/resources/cursor/rules/project-profiling.mdc +197 -0
- tapps_agents/resources/cursor/rules/quick-reference.mdc +630 -0
- tapps_agents/resources/cursor/rules/security.mdc +32 -0
- tapps_agents/resources/cursor/rules/simple-mode.mdc +500 -0
- tapps_agents/resources/cursor/rules/testing.mdc +31 -0
- tapps_agents/resources/cursor/rules/when-to-use.mdc +156 -0
- tapps_agents/resources/cursor/rules/workflow-presets.mdc +179 -0
- tapps_agents/resources/customizations/__init__.py +1 -0
- tapps_agents/resources/customizations/example-custom.yaml +83 -0
- tapps_agents/resources/hooks/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/README.md +5 -0
- tapps_agents/resources/hooks/templates/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/add-project-context.yaml +8 -0
- tapps_agents/resources/hooks/templates/auto-format-js.yaml +10 -0
- tapps_agents/resources/hooks/templates/auto-format-python.yaml +10 -0
- tapps_agents/resources/hooks/templates/git-commit-check.yaml +7 -0
- tapps_agents/resources/hooks/templates/notify-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/quality-gate.yaml +8 -0
- tapps_agents/resources/hooks/templates/security-scan-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/session-end-log.yaml +7 -0
- tapps_agents/resources/hooks/templates/show-beads-ready.yaml +8 -0
- tapps_agents/resources/hooks/templates/test-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/update-docs-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/user-prompt-log.yaml +7 -0
- tapps_agents/resources/scripts/__init__.py +1 -0
- tapps_agents/resources/scripts/set_bd_path.ps1 +51 -0
- tapps_agents/resources/workflows/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/brownfield-analysis.yaml +235 -0
- tapps_agents/resources/workflows/presets/fix.yaml +78 -0
- tapps_agents/resources/workflows/presets/full-sdlc.yaml +122 -0
- tapps_agents/resources/workflows/presets/quality.yaml +82 -0
- tapps_agents/resources/workflows/presets/rapid-dev.yaml +84 -0
- tapps_agents/simple_mode/orchestrators/base.py +185 -185
- tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2700 -2667
- tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
- tapps_agents/workflow/cursor_executor.py +2337 -2337
- tapps_agents/workflow/message_formatter.py +188 -188
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/METADATA +6 -6
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/RECORD +141 -18
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/licenses/LICENSE +0 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Artifact Context Builder - Token-aware artifact injection with budgeting.
|
|
3
|
+
|
|
4
|
+
Manages workflow artifact context with token budgets and priority ordering
|
|
5
|
+
to prevent context window overflow when injecting prior step documentation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import re
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class ArtifactEntry:
|
|
18
|
+
"""Single artifact entry with metadata."""
|
|
19
|
+
|
|
20
|
+
key: str
|
|
21
|
+
content: str
|
|
22
|
+
token_estimate: int
|
|
23
|
+
priority: int
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ArtifactContextBuilder:
|
|
27
|
+
"""
|
|
28
|
+
Builds context from workflow artifacts with token budget enforcement.
|
|
29
|
+
|
|
30
|
+
Fills artifacts in priority order (spec → user_stories → architecture → api_design)
|
|
31
|
+
and applies token budgets to prevent context overflow. When over budget, either
|
|
32
|
+
truncates content to fit or uses template summaries.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> artifacts = [
|
|
36
|
+
... ("specification", "Enhanced prompt content...", 1),
|
|
37
|
+
... ("user_stories", "User stories content...", 2),
|
|
38
|
+
... ("architecture", "Architecture content...", 3),
|
|
39
|
+
... ("api_design", "API design content...", 4),
|
|
40
|
+
... ]
|
|
41
|
+
>>> builder = ArtifactContextBuilder(token_budget=4000)
|
|
42
|
+
>>> result = builder.build_context(artifacts)
|
|
43
|
+
>>> # Returns dict with full content or summaries based on budget
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
token_budget: int = 4000,
|
|
49
|
+
use_tiktoken: bool = True,
|
|
50
|
+
summarization_enabled: bool = False,
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Initialize ArtifactContextBuilder.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
token_budget: Maximum tokens for all artifacts combined
|
|
57
|
+
use_tiktoken: Try to use tiktoken for accurate estimation (fallback to chars/4)
|
|
58
|
+
summarization_enabled: Use template summaries when over budget (default: truncate)
|
|
59
|
+
"""
|
|
60
|
+
self.token_budget = token_budget
|
|
61
|
+
self.use_tiktoken = use_tiktoken
|
|
62
|
+
self.summarization_enabled = summarization_enabled
|
|
63
|
+
self._tiktoken_encoder = None
|
|
64
|
+
|
|
65
|
+
# Try to load tiktoken if requested
|
|
66
|
+
if use_tiktoken:
|
|
67
|
+
try:
|
|
68
|
+
import tiktoken
|
|
69
|
+
self._tiktoken_encoder = tiktoken.get_encoding("cl100k_base")
|
|
70
|
+
logger.debug("Using tiktoken for token estimation")
|
|
71
|
+
except ImportError:
|
|
72
|
+
logger.debug("tiktoken not available, falling back to chars/4 estimation")
|
|
73
|
+
|
|
74
|
+
# Template summaries for each artifact type
|
|
75
|
+
self._summary_templates = {
|
|
76
|
+
"specification": "Enhanced prompt (summary)",
|
|
77
|
+
"user_stories": "User stories: {count} items",
|
|
78
|
+
"architecture": "Architecture: {summary}",
|
|
79
|
+
"api_design": "API design: {count} endpoints",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def estimate_tokens(self, text: str) -> int:
|
|
83
|
+
"""
|
|
84
|
+
Estimate token count for text.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
text: Text to estimate
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Estimated token count
|
|
91
|
+
"""
|
|
92
|
+
if not text:
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
if self._tiktoken_encoder:
|
|
96
|
+
try:
|
|
97
|
+
return len(self._tiktoken_encoder.encode(text))
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.warning(f"tiktoken encoding failed, falling back to chars/4: {e}")
|
|
100
|
+
|
|
101
|
+
# Fallback: chars/4 with small safety margin
|
|
102
|
+
return int(len(text) / 4 * 1.1) # 10% safety margin
|
|
103
|
+
|
|
104
|
+
def _extract_summary_info(self, key: str, content: str) -> dict[str, Any]:
|
|
105
|
+
"""
|
|
106
|
+
Extract summary information from content.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
key: Artifact key
|
|
110
|
+
content: Artifact content
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Dictionary with summary info (count, summary text, etc.)
|
|
114
|
+
"""
|
|
115
|
+
info: dict[str, Any] = {}
|
|
116
|
+
|
|
117
|
+
if key == "user_stories":
|
|
118
|
+
# Count user stories (look for "US-" or "Story" or numbered lists)
|
|
119
|
+
story_matches = re.findall(r'(?:US-\d+|Story \d+|^\d+\.)', content, re.MULTILINE)
|
|
120
|
+
info["count"] = len(story_matches) if story_matches else 1
|
|
121
|
+
|
|
122
|
+
elif key == "api_design":
|
|
123
|
+
# Count API endpoints (look for HTTP methods or endpoint patterns)
|
|
124
|
+
endpoint_matches = re.findall(
|
|
125
|
+
r'(?:GET|POST|PUT|DELETE|PATCH)\s+/|endpoint\s*:|route\s*:',
|
|
126
|
+
content,
|
|
127
|
+
re.IGNORECASE
|
|
128
|
+
)
|
|
129
|
+
info["count"] = len(endpoint_matches) if endpoint_matches else 1
|
|
130
|
+
|
|
131
|
+
elif key == "architecture":
|
|
132
|
+
# Extract first sentence or first line as summary
|
|
133
|
+
lines = content.strip().split('\n')
|
|
134
|
+
first_line = lines[0] if lines else "Component architecture"
|
|
135
|
+
# Clean markdown and limit length
|
|
136
|
+
summary = re.sub(r'[#*_`]', '', first_line)[:100]
|
|
137
|
+
info["summary"] = summary or "System architecture"
|
|
138
|
+
|
|
139
|
+
return info
|
|
140
|
+
|
|
141
|
+
def _generate_summary(self, key: str, content: str) -> str:
|
|
142
|
+
"""
|
|
143
|
+
Generate template summary for artifact.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
key: Artifact key
|
|
147
|
+
content: Original content
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Template summary string
|
|
151
|
+
"""
|
|
152
|
+
template = self._summary_templates.get(key, f"{key} (summary)")
|
|
153
|
+
|
|
154
|
+
# Extract info for template formatting
|
|
155
|
+
info = self._extract_summary_info(key, content)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
return template.format(**info)
|
|
159
|
+
except (KeyError, ValueError):
|
|
160
|
+
# Fallback if template formatting fails
|
|
161
|
+
return template
|
|
162
|
+
|
|
163
|
+
def build_context(
|
|
164
|
+
self,
|
|
165
|
+
artifacts: list[tuple[str, str, int]] | list[tuple[str, str]],
|
|
166
|
+
) -> dict[str, str]:
|
|
167
|
+
"""
|
|
168
|
+
Build context from ordered artifacts with token budget enforcement.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
artifacts: List of (key, content, priority) or (key, content) tuples.
|
|
172
|
+
If priority not provided, uses order in list (1, 2, 3, ...).
|
|
173
|
+
Priority 1 = highest priority (filled first)
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Dictionary mapping keys to content (full or summary/truncated)
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
>>> artifacts = [
|
|
180
|
+
... ("specification", "Long spec...", 1),
|
|
181
|
+
... ("user_stories", "Stories...", 2),
|
|
182
|
+
... ("architecture", "Arch...", 3),
|
|
183
|
+
... ("api_design", "API...", 4),
|
|
184
|
+
... ]
|
|
185
|
+
>>> result = builder.build_context(artifacts)
|
|
186
|
+
>>> # Result contains full content up to budget, then summaries
|
|
187
|
+
"""
|
|
188
|
+
if not artifacts:
|
|
189
|
+
return {}
|
|
190
|
+
|
|
191
|
+
# Normalize to include priority
|
|
192
|
+
normalized_artifacts: list[ArtifactEntry] = []
|
|
193
|
+
for i, artifact in enumerate(artifacts):
|
|
194
|
+
if len(artifact) == 2:
|
|
195
|
+
key, content = artifact
|
|
196
|
+
priority = i + 1 # Use order as priority
|
|
197
|
+
else:
|
|
198
|
+
key, content, priority = artifact
|
|
199
|
+
|
|
200
|
+
token_estimate = self.estimate_tokens(content)
|
|
201
|
+
normalized_artifacts.append(
|
|
202
|
+
ArtifactEntry(
|
|
203
|
+
key=key,
|
|
204
|
+
content=content,
|
|
205
|
+
token_estimate=token_estimate,
|
|
206
|
+
priority=priority,
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Sort by priority (lower priority number = higher priority)
|
|
211
|
+
normalized_artifacts.sort(key=lambda x: x.priority)
|
|
212
|
+
|
|
213
|
+
# Build context respecting budget
|
|
214
|
+
result: dict[str, str] = {}
|
|
215
|
+
tokens_used = 0
|
|
216
|
+
|
|
217
|
+
for entry in normalized_artifacts:
|
|
218
|
+
remaining_budget = self.token_budget - tokens_used
|
|
219
|
+
|
|
220
|
+
if remaining_budget <= 0:
|
|
221
|
+
# Budget exhausted - use summary if enabled
|
|
222
|
+
if self.summarization_enabled:
|
|
223
|
+
summary = self._generate_summary(entry.key, entry.content)
|
|
224
|
+
result[entry.key] = summary
|
|
225
|
+
tokens_used += self.estimate_tokens(summary)
|
|
226
|
+
logger.debug(
|
|
227
|
+
f"Budget exhausted for '{entry.key}', using summary "
|
|
228
|
+
f"({self.estimate_tokens(summary)} tokens)"
|
|
229
|
+
)
|
|
230
|
+
else:
|
|
231
|
+
# Skip artifact entirely
|
|
232
|
+
logger.debug(f"Budget exhausted, skipping '{entry.key}'")
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
if entry.token_estimate <= remaining_budget:
|
|
236
|
+
# Fits in budget - use full content
|
|
237
|
+
result[entry.key] = entry.content
|
|
238
|
+
tokens_used += entry.token_estimate
|
|
239
|
+
logger.debug(
|
|
240
|
+
f"Added '{entry.key}' ({entry.token_estimate} tokens, "
|
|
241
|
+
f"{tokens_used}/{self.token_budget} used)"
|
|
242
|
+
)
|
|
243
|
+
else:
|
|
244
|
+
# Doesn't fit - truncate or summarize
|
|
245
|
+
if self.summarization_enabled:
|
|
246
|
+
# Use summary
|
|
247
|
+
summary = self._generate_summary(entry.key, entry.content)
|
|
248
|
+
summary_tokens = self.estimate_tokens(summary)
|
|
249
|
+
|
|
250
|
+
if summary_tokens <= remaining_budget:
|
|
251
|
+
result[entry.key] = summary
|
|
252
|
+
tokens_used += summary_tokens
|
|
253
|
+
logger.debug(
|
|
254
|
+
f"'{entry.key}' over budget ({entry.token_estimate} tokens), "
|
|
255
|
+
f"using summary ({summary_tokens} tokens)"
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
# Even summary doesn't fit - skip
|
|
259
|
+
logger.debug(
|
|
260
|
+
f"'{entry.key}' summary too large ({summary_tokens} tokens), skipping"
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
# Truncate to fit remaining budget
|
|
264
|
+
# Estimate characters for remaining tokens (inverse of estimation)
|
|
265
|
+
if self._tiktoken_encoder:
|
|
266
|
+
# With tiktoken, we need to truncate and re-encode iteratively
|
|
267
|
+
# For simplicity, use chars/4 estimate to get approximate length
|
|
268
|
+
target_chars = int(remaining_budget * 4 / 1.1)
|
|
269
|
+
else:
|
|
270
|
+
target_chars = int(remaining_budget * 4 / 1.1)
|
|
271
|
+
|
|
272
|
+
truncated = entry.content[:target_chars]
|
|
273
|
+
# Clean up - don't truncate mid-sentence if possible
|
|
274
|
+
last_period = truncated.rfind('.')
|
|
275
|
+
if last_period > target_chars * 0.7: # Within last 30%
|
|
276
|
+
truncated = truncated[:last_period + 1]
|
|
277
|
+
|
|
278
|
+
truncated += "\n\n[Content truncated to fit token budget]"
|
|
279
|
+
|
|
280
|
+
result[entry.key] = truncated
|
|
281
|
+
truncated_tokens = self.estimate_tokens(truncated)
|
|
282
|
+
tokens_used += truncated_tokens
|
|
283
|
+
logger.debug(
|
|
284
|
+
f"Truncated '{entry.key}' from {entry.token_estimate} "
|
|
285
|
+
f"to {truncated_tokens} tokens"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
logger.info(
|
|
289
|
+
f"Built context with {len(result)}/{len(normalized_artifacts)} artifacts, "
|
|
290
|
+
f"{tokens_used}/{self.token_budget} tokens used"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return result
|
tapps_agents/core/config.py
CHANGED
|
@@ -390,6 +390,29 @@ class Context7Config(BaseModel):
|
|
|
390
390
|
)
|
|
391
391
|
refresh: Context7RefreshConfig = Field(default_factory=Context7RefreshConfig)
|
|
392
392
|
|
|
393
|
+
# Option 3: Strict On-Demand Usage and Per-Agent Caps
|
|
394
|
+
require_topic: bool = Field(
|
|
395
|
+
default=True,
|
|
396
|
+
description="Require topic when fetching Context7 documentation (prevents 'whole library' fetches)"
|
|
397
|
+
)
|
|
398
|
+
allow_full_library_libraries: list[str] = Field(
|
|
399
|
+
default_factory=list,
|
|
400
|
+
description="Libraries allowed to fetch without topic (e.g., ['python-stdlib'])"
|
|
401
|
+
)
|
|
402
|
+
per_agent_max_tokens: dict[str, int] = Field(
|
|
403
|
+
default_factory=lambda: {
|
|
404
|
+
"architect": 4000,
|
|
405
|
+
"implementer": 3000,
|
|
406
|
+
"tester": 2500,
|
|
407
|
+
"reviewer": 3000,
|
|
408
|
+
"analyst": 2000,
|
|
409
|
+
"planner": 2000,
|
|
410
|
+
"designer": 3000,
|
|
411
|
+
"default": 2500,
|
|
412
|
+
},
|
|
413
|
+
description="Maximum Context7 tokens per agent per turn"
|
|
414
|
+
)
|
|
415
|
+
|
|
393
416
|
|
|
394
417
|
class QualityToolsConfig(BaseModel):
|
|
395
418
|
"""Configuration for quality analysis tools (Phase 6 - 2025 Standards)"""
|
|
@@ -1193,6 +1216,16 @@ class SimpleModeConfig(BaseModel):
|
|
|
1193
1216
|
le=1.0,
|
|
1194
1217
|
description="Minimum confidence threshold for checkpoint recommendations (0.0-1.0)",
|
|
1195
1218
|
)
|
|
1219
|
+
artifact_context_budget_tokens: int = Field(
|
|
1220
|
+
default=4000,
|
|
1221
|
+
ge=1000,
|
|
1222
|
+
le=16000,
|
|
1223
|
+
description="Token budget for workflow artifact context (spec, user_stories, architecture, api_design)",
|
|
1224
|
+
)
|
|
1225
|
+
artifact_summarization_enabled: bool = Field(
|
|
1226
|
+
default=False,
|
|
1227
|
+
description="Use template summaries when artifact budget is exceeded (default: truncate)",
|
|
1228
|
+
)
|
|
1196
1229
|
|
|
1197
1230
|
|
|
1198
1231
|
class AutoEnhancementConfig(BaseModel):
|