foundry-mcp 0.8.22__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.
Potentially problematic release.
This version of foundry-mcp might be problematic. Click here for more details.
- foundry_mcp/__init__.py +13 -0
- foundry_mcp/cli/__init__.py +67 -0
- foundry_mcp/cli/__main__.py +9 -0
- foundry_mcp/cli/agent.py +96 -0
- foundry_mcp/cli/commands/__init__.py +37 -0
- foundry_mcp/cli/commands/cache.py +137 -0
- foundry_mcp/cli/commands/dashboard.py +148 -0
- foundry_mcp/cli/commands/dev.py +446 -0
- foundry_mcp/cli/commands/journal.py +377 -0
- foundry_mcp/cli/commands/lifecycle.py +274 -0
- foundry_mcp/cli/commands/modify.py +824 -0
- foundry_mcp/cli/commands/plan.py +640 -0
- foundry_mcp/cli/commands/pr.py +393 -0
- foundry_mcp/cli/commands/review.py +667 -0
- foundry_mcp/cli/commands/session.py +472 -0
- foundry_mcp/cli/commands/specs.py +686 -0
- foundry_mcp/cli/commands/tasks.py +807 -0
- foundry_mcp/cli/commands/testing.py +676 -0
- foundry_mcp/cli/commands/validate.py +982 -0
- foundry_mcp/cli/config.py +98 -0
- foundry_mcp/cli/context.py +298 -0
- foundry_mcp/cli/logging.py +212 -0
- foundry_mcp/cli/main.py +44 -0
- foundry_mcp/cli/output.py +122 -0
- foundry_mcp/cli/registry.py +110 -0
- foundry_mcp/cli/resilience.py +178 -0
- foundry_mcp/cli/transcript.py +217 -0
- foundry_mcp/config.py +1454 -0
- foundry_mcp/core/__init__.py +144 -0
- foundry_mcp/core/ai_consultation.py +1773 -0
- foundry_mcp/core/batch_operations.py +1202 -0
- foundry_mcp/core/cache.py +195 -0
- foundry_mcp/core/capabilities.py +446 -0
- foundry_mcp/core/concurrency.py +898 -0
- foundry_mcp/core/context.py +540 -0
- foundry_mcp/core/discovery.py +1603 -0
- foundry_mcp/core/error_collection.py +728 -0
- foundry_mcp/core/error_store.py +592 -0
- foundry_mcp/core/health.py +749 -0
- foundry_mcp/core/intake.py +933 -0
- foundry_mcp/core/journal.py +700 -0
- foundry_mcp/core/lifecycle.py +412 -0
- foundry_mcp/core/llm_config.py +1376 -0
- foundry_mcp/core/llm_patterns.py +510 -0
- foundry_mcp/core/llm_provider.py +1569 -0
- foundry_mcp/core/logging_config.py +374 -0
- foundry_mcp/core/metrics_persistence.py +584 -0
- foundry_mcp/core/metrics_registry.py +327 -0
- foundry_mcp/core/metrics_store.py +641 -0
- foundry_mcp/core/modifications.py +224 -0
- foundry_mcp/core/naming.py +146 -0
- foundry_mcp/core/observability.py +1216 -0
- foundry_mcp/core/otel.py +452 -0
- foundry_mcp/core/otel_stubs.py +264 -0
- foundry_mcp/core/pagination.py +255 -0
- foundry_mcp/core/progress.py +387 -0
- foundry_mcp/core/prometheus.py +564 -0
- foundry_mcp/core/prompts/__init__.py +464 -0
- foundry_mcp/core/prompts/fidelity_review.py +691 -0
- foundry_mcp/core/prompts/markdown_plan_review.py +515 -0
- foundry_mcp/core/prompts/plan_review.py +627 -0
- foundry_mcp/core/providers/__init__.py +237 -0
- foundry_mcp/core/providers/base.py +515 -0
- foundry_mcp/core/providers/claude.py +472 -0
- foundry_mcp/core/providers/codex.py +637 -0
- foundry_mcp/core/providers/cursor_agent.py +630 -0
- foundry_mcp/core/providers/detectors.py +515 -0
- foundry_mcp/core/providers/gemini.py +426 -0
- foundry_mcp/core/providers/opencode.py +718 -0
- foundry_mcp/core/providers/opencode_wrapper.js +308 -0
- foundry_mcp/core/providers/package-lock.json +24 -0
- foundry_mcp/core/providers/package.json +25 -0
- foundry_mcp/core/providers/registry.py +607 -0
- foundry_mcp/core/providers/test_provider.py +171 -0
- foundry_mcp/core/providers/validation.py +857 -0
- foundry_mcp/core/rate_limit.py +427 -0
- foundry_mcp/core/research/__init__.py +68 -0
- foundry_mcp/core/research/memory.py +528 -0
- foundry_mcp/core/research/models.py +1234 -0
- foundry_mcp/core/research/providers/__init__.py +40 -0
- foundry_mcp/core/research/providers/base.py +242 -0
- foundry_mcp/core/research/providers/google.py +507 -0
- foundry_mcp/core/research/providers/perplexity.py +442 -0
- foundry_mcp/core/research/providers/semantic_scholar.py +544 -0
- foundry_mcp/core/research/providers/tavily.py +383 -0
- foundry_mcp/core/research/workflows/__init__.py +25 -0
- foundry_mcp/core/research/workflows/base.py +298 -0
- foundry_mcp/core/research/workflows/chat.py +271 -0
- foundry_mcp/core/research/workflows/consensus.py +539 -0
- foundry_mcp/core/research/workflows/deep_research.py +4142 -0
- foundry_mcp/core/research/workflows/ideate.py +682 -0
- foundry_mcp/core/research/workflows/thinkdeep.py +405 -0
- foundry_mcp/core/resilience.py +600 -0
- foundry_mcp/core/responses.py +1624 -0
- foundry_mcp/core/review.py +366 -0
- foundry_mcp/core/security.py +438 -0
- foundry_mcp/core/spec.py +4119 -0
- foundry_mcp/core/task.py +2463 -0
- foundry_mcp/core/testing.py +839 -0
- foundry_mcp/core/validation.py +2357 -0
- foundry_mcp/dashboard/__init__.py +32 -0
- foundry_mcp/dashboard/app.py +119 -0
- foundry_mcp/dashboard/components/__init__.py +17 -0
- foundry_mcp/dashboard/components/cards.py +88 -0
- foundry_mcp/dashboard/components/charts.py +177 -0
- foundry_mcp/dashboard/components/filters.py +136 -0
- foundry_mcp/dashboard/components/tables.py +195 -0
- foundry_mcp/dashboard/data/__init__.py +11 -0
- foundry_mcp/dashboard/data/stores.py +433 -0
- foundry_mcp/dashboard/launcher.py +300 -0
- foundry_mcp/dashboard/views/__init__.py +12 -0
- foundry_mcp/dashboard/views/errors.py +217 -0
- foundry_mcp/dashboard/views/metrics.py +164 -0
- foundry_mcp/dashboard/views/overview.py +96 -0
- foundry_mcp/dashboard/views/providers.py +83 -0
- foundry_mcp/dashboard/views/sdd_workflow.py +255 -0
- foundry_mcp/dashboard/views/tool_usage.py +139 -0
- foundry_mcp/prompts/__init__.py +9 -0
- foundry_mcp/prompts/workflows.py +525 -0
- foundry_mcp/resources/__init__.py +9 -0
- foundry_mcp/resources/specs.py +591 -0
- foundry_mcp/schemas/__init__.py +38 -0
- foundry_mcp/schemas/intake-schema.json +89 -0
- foundry_mcp/schemas/sdd-spec-schema.json +414 -0
- foundry_mcp/server.py +150 -0
- foundry_mcp/tools/__init__.py +10 -0
- foundry_mcp/tools/unified/__init__.py +92 -0
- foundry_mcp/tools/unified/authoring.py +3620 -0
- foundry_mcp/tools/unified/context_helpers.py +98 -0
- foundry_mcp/tools/unified/documentation_helpers.py +268 -0
- foundry_mcp/tools/unified/environment.py +1341 -0
- foundry_mcp/tools/unified/error.py +479 -0
- foundry_mcp/tools/unified/health.py +225 -0
- foundry_mcp/tools/unified/journal.py +841 -0
- foundry_mcp/tools/unified/lifecycle.py +640 -0
- foundry_mcp/tools/unified/metrics.py +777 -0
- foundry_mcp/tools/unified/plan.py +876 -0
- foundry_mcp/tools/unified/pr.py +294 -0
- foundry_mcp/tools/unified/provider.py +589 -0
- foundry_mcp/tools/unified/research.py +1283 -0
- foundry_mcp/tools/unified/review.py +1042 -0
- foundry_mcp/tools/unified/review_helpers.py +314 -0
- foundry_mcp/tools/unified/router.py +102 -0
- foundry_mcp/tools/unified/server.py +565 -0
- foundry_mcp/tools/unified/spec.py +1283 -0
- foundry_mcp/tools/unified/task.py +3846 -0
- foundry_mcp/tools/unified/test.py +431 -0
- foundry_mcp/tools/unified/verification.py +520 -0
- foundry_mcp-0.8.22.dist-info/METADATA +344 -0
- foundry_mcp-0.8.22.dist-info/RECORD +153 -0
- foundry_mcp-0.8.22.dist-info/WHEEL +4 -0
- foundry_mcp-0.8.22.dist-info/entry_points.txt +3 -0
- foundry_mcp-0.8.22.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt templates and builders for AI consultation workflows.
|
|
3
|
+
|
|
4
|
+
This package provides:
|
|
5
|
+
1. PromptTemplate dataclass for defining structured prompts
|
|
6
|
+
2. PromptRegistry for registering and retrieving prompts by ID
|
|
7
|
+
3. Workflow-specific prompt builders for different consultation use cases
|
|
8
|
+
|
|
9
|
+
Workflow Coverage:
|
|
10
|
+
- plan_review: Review and critique SDD specifications
|
|
11
|
+
- fidelity_review: Compare implementation against specifications
|
|
12
|
+
|
|
13
|
+
Example Usage:
|
|
14
|
+
from foundry_mcp.core.prompts import (
|
|
15
|
+
PromptTemplate,
|
|
16
|
+
PromptRegistry,
|
|
17
|
+
get_prompt_builder,
|
|
18
|
+
)
|
|
19
|
+
from foundry_mcp.core.ai_consultation import ConsultationWorkflow
|
|
20
|
+
|
|
21
|
+
# Register a custom prompt template
|
|
22
|
+
template = PromptTemplate(
|
|
23
|
+
id="custom_analysis",
|
|
24
|
+
version="1.0",
|
|
25
|
+
system_prompt="You are an expert code reviewer.",
|
|
26
|
+
user_template="Analyze the following code: {code}",
|
|
27
|
+
required_context=["code"],
|
|
28
|
+
)
|
|
29
|
+
registry = PromptRegistry()
|
|
30
|
+
registry.register(template)
|
|
31
|
+
|
|
32
|
+
# Or use workflow-specific builders
|
|
33
|
+
builder = get_prompt_builder(ConsultationWorkflow.PLAN_REVIEW)
|
|
34
|
+
prompt = builder.build("spec_review", {"spec_content": "..."})
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import logging
|
|
40
|
+
import re
|
|
41
|
+
from abc import ABC, abstractmethod
|
|
42
|
+
from dataclasses import dataclass, field
|
|
43
|
+
from typing import Any, Dict, List, Optional, Set, TYPE_CHECKING
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from foundry_mcp.core.ai_consultation import ConsultationWorkflow
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# PromptTemplate Dataclass
|
|
53
|
+
# =============================================================================
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class PromptTemplate:
|
|
58
|
+
"""
|
|
59
|
+
Structured prompt template for AI consultations.
|
|
60
|
+
|
|
61
|
+
Defines a reusable prompt with system and user components, required
|
|
62
|
+
context variables, and metadata for tracking and versioning.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
id: Unique identifier for the template (e.g., "analyze_module")
|
|
66
|
+
version: Template version for tracking changes (e.g., "1.0", "2.1")
|
|
67
|
+
system_prompt: System/instruction prompt (sent as system message)
|
|
68
|
+
user_template: User message template with {variable} placeholders
|
|
69
|
+
required_context: List of required context keys for rendering
|
|
70
|
+
optional_context: List of optional context keys (have defaults)
|
|
71
|
+
metadata: Additional template metadata (author, tags, etc.)
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
template = PromptTemplate(
|
|
75
|
+
id="analyze_module",
|
|
76
|
+
version="1.0",
|
|
77
|
+
system_prompt="You are an expert Python developer.",
|
|
78
|
+
user_template="Analyze this module:\\n\\n{code}\\n\\nFile: {file_path}",
|
|
79
|
+
required_context=["code", "file_path"],
|
|
80
|
+
metadata={"author": "system", "category": "documentation"},
|
|
81
|
+
)
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
id: str
|
|
85
|
+
version: str
|
|
86
|
+
system_prompt: str
|
|
87
|
+
user_template: str
|
|
88
|
+
required_context: List[str] = field(default_factory=list)
|
|
89
|
+
optional_context: List[str] = field(default_factory=list)
|
|
90
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
91
|
+
|
|
92
|
+
def __post_init__(self) -> None:
|
|
93
|
+
"""Validate template after initialization."""
|
|
94
|
+
if not self.id:
|
|
95
|
+
raise ValueError("Template id cannot be empty")
|
|
96
|
+
if not self.version:
|
|
97
|
+
raise ValueError("Template version cannot be empty")
|
|
98
|
+
if not self.user_template:
|
|
99
|
+
raise ValueError("Template user_template cannot be empty")
|
|
100
|
+
|
|
101
|
+
def get_variables(self) -> Set[str]:
|
|
102
|
+
"""
|
|
103
|
+
Extract all variable names from the user template.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Set of variable names found in {variable} placeholders
|
|
107
|
+
"""
|
|
108
|
+
# Find all {variable} patterns, excluding {{ and }}
|
|
109
|
+
pattern = r'\{([a-zA-Z_][a-zA-Z0-9_]*)\}'
|
|
110
|
+
return set(re.findall(pattern, self.user_template))
|
|
111
|
+
|
|
112
|
+
def validate_context(self, context: Dict[str, Any]) -> List[str]:
|
|
113
|
+
"""
|
|
114
|
+
Validate that context contains all required keys.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
context: Context dict to validate
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
List of missing required keys (empty if valid)
|
|
121
|
+
"""
|
|
122
|
+
missing = []
|
|
123
|
+
for key in self.required_context:
|
|
124
|
+
if key not in context:
|
|
125
|
+
missing.append(key)
|
|
126
|
+
return missing
|
|
127
|
+
|
|
128
|
+
def render(
|
|
129
|
+
self,
|
|
130
|
+
context: Dict[str, Any],
|
|
131
|
+
*,
|
|
132
|
+
strict: bool = True,
|
|
133
|
+
defaults: Optional[Dict[str, Any]] = None,
|
|
134
|
+
) -> str:
|
|
135
|
+
"""
|
|
136
|
+
Render the user template with context substitution.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
context: Context dict with variable values
|
|
140
|
+
strict: If True, raise ValueError for missing required keys
|
|
141
|
+
defaults: Default values for optional context keys
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Rendered template string
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
ValueError: If strict=True and required keys are missing
|
|
148
|
+
"""
|
|
149
|
+
# Validate required context
|
|
150
|
+
missing = self.validate_context(context)
|
|
151
|
+
if missing and strict:
|
|
152
|
+
raise ValueError(
|
|
153
|
+
f"Missing required context keys for template '{self.id}': {missing}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Build render context with defaults
|
|
157
|
+
render_context = dict(defaults or {})
|
|
158
|
+
render_context.update(context)
|
|
159
|
+
|
|
160
|
+
# Provide empty string for missing optional keys
|
|
161
|
+
for key in self.optional_context:
|
|
162
|
+
if key not in render_context:
|
|
163
|
+
render_context[key] = ""
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
return self.user_template.format(**render_context)
|
|
167
|
+
except KeyError as exc:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"Missing context key for template '{self.id}': {exc}"
|
|
170
|
+
) from exc
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# =============================================================================
|
|
174
|
+
# PromptRegistry
|
|
175
|
+
# =============================================================================
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class PromptRegistry:
|
|
179
|
+
"""
|
|
180
|
+
Registry for managing prompt templates.
|
|
181
|
+
|
|
182
|
+
Provides registration, retrieval, and listing of prompt templates
|
|
183
|
+
by ID, with optional namespace support for organizing templates.
|
|
184
|
+
|
|
185
|
+
Attributes:
|
|
186
|
+
templates: Dict mapping template IDs to PromptTemplate instances
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
registry = PromptRegistry()
|
|
190
|
+
|
|
191
|
+
# Register a template
|
|
192
|
+
template = PromptTemplate(
|
|
193
|
+
id="analyze_code",
|
|
194
|
+
version="1.0",
|
|
195
|
+
system_prompt="You are a code analyst.",
|
|
196
|
+
user_template="Analyze: {code}",
|
|
197
|
+
required_context=["code"],
|
|
198
|
+
)
|
|
199
|
+
registry.register(template)
|
|
200
|
+
|
|
201
|
+
# Retrieve and use
|
|
202
|
+
t = registry.get("analyze_code")
|
|
203
|
+
prompt = t.render({"code": "def foo(): pass"})
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
def __init__(self) -> None:
|
|
207
|
+
"""Initialize an empty registry."""
|
|
208
|
+
self._templates: Dict[str, PromptTemplate] = {}
|
|
209
|
+
|
|
210
|
+
def register(
|
|
211
|
+
self,
|
|
212
|
+
template: PromptTemplate,
|
|
213
|
+
*,
|
|
214
|
+
replace: bool = False,
|
|
215
|
+
) -> None:
|
|
216
|
+
"""
|
|
217
|
+
Register a prompt template.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
template: The PromptTemplate to register
|
|
221
|
+
replace: If True, replace existing template with same ID
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
ValueError: If template ID already registered and replace=False
|
|
225
|
+
"""
|
|
226
|
+
if template.id in self._templates and not replace:
|
|
227
|
+
raise ValueError(
|
|
228
|
+
f"Template '{template.id}' is already registered. "
|
|
229
|
+
"Use replace=True to overwrite."
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
self._templates[template.id] = template
|
|
233
|
+
logger.debug(
|
|
234
|
+
"Registered prompt template '%s' (version %s)",
|
|
235
|
+
template.id,
|
|
236
|
+
template.version,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def get(self, template_id: str) -> Optional[PromptTemplate]:
|
|
240
|
+
"""
|
|
241
|
+
Retrieve a template by ID.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
template_id: The template identifier
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
PromptTemplate if found, None otherwise
|
|
248
|
+
"""
|
|
249
|
+
return self._templates.get(template_id)
|
|
250
|
+
|
|
251
|
+
def get_required(self, template_id: str) -> PromptTemplate:
|
|
252
|
+
"""
|
|
253
|
+
Retrieve a template by ID, raising if not found.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
template_id: The template identifier
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
The PromptTemplate
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
KeyError: If template not found
|
|
263
|
+
"""
|
|
264
|
+
template = self._templates.get(template_id)
|
|
265
|
+
if template is None:
|
|
266
|
+
available = ", ".join(sorted(self._templates.keys())) or "(none)"
|
|
267
|
+
raise KeyError(
|
|
268
|
+
f"Template '{template_id}' not found. Available: {available}"
|
|
269
|
+
)
|
|
270
|
+
return template
|
|
271
|
+
|
|
272
|
+
def list_templates(self) -> List[str]:
|
|
273
|
+
"""
|
|
274
|
+
Return list of registered template IDs.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Sorted list of template IDs
|
|
278
|
+
"""
|
|
279
|
+
return sorted(self._templates.keys())
|
|
280
|
+
|
|
281
|
+
def unregister(self, template_id: str) -> bool:
|
|
282
|
+
"""
|
|
283
|
+
Remove a template from the registry.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
template_id: The template identifier
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
True if template was removed, False if not found
|
|
290
|
+
"""
|
|
291
|
+
if template_id in self._templates:
|
|
292
|
+
del self._templates[template_id]
|
|
293
|
+
logger.debug("Unregistered prompt template '%s'", template_id)
|
|
294
|
+
return True
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
def clear(self) -> None:
|
|
298
|
+
"""Remove all templates from the registry."""
|
|
299
|
+
self._templates.clear()
|
|
300
|
+
logger.debug("Cleared all prompt templates from registry")
|
|
301
|
+
|
|
302
|
+
def render(
|
|
303
|
+
self,
|
|
304
|
+
template_id: str,
|
|
305
|
+
context: Dict[str, Any],
|
|
306
|
+
*,
|
|
307
|
+
strict: bool = True,
|
|
308
|
+
) -> str:
|
|
309
|
+
"""
|
|
310
|
+
Render a template by ID with context.
|
|
311
|
+
|
|
312
|
+
Convenience method combining get_required() and render().
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
template_id: The template identifier
|
|
316
|
+
context: Context dict for rendering
|
|
317
|
+
strict: If True, raise for missing required keys
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Rendered template string
|
|
321
|
+
|
|
322
|
+
Raises:
|
|
323
|
+
KeyError: If template not found
|
|
324
|
+
ValueError: If strict=True and required keys missing
|
|
325
|
+
"""
|
|
326
|
+
template = self.get_required(template_id)
|
|
327
|
+
return template.render(context, strict=strict)
|
|
328
|
+
|
|
329
|
+
def __len__(self) -> int:
|
|
330
|
+
"""Return number of registered templates."""
|
|
331
|
+
return len(self._templates)
|
|
332
|
+
|
|
333
|
+
def __contains__(self, template_id: str) -> bool:
|
|
334
|
+
"""Check if template ID is registered."""
|
|
335
|
+
return template_id in self._templates
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
# =============================================================================
|
|
339
|
+
# Global Registry Instance
|
|
340
|
+
# =============================================================================
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
# Default global registry for application-wide templates
|
|
344
|
+
_global_registry: Optional[PromptRegistry] = None
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def get_global_registry() -> PromptRegistry:
|
|
348
|
+
"""
|
|
349
|
+
Get the global prompt registry singleton.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
The global PromptRegistry instance
|
|
353
|
+
"""
|
|
354
|
+
global _global_registry
|
|
355
|
+
if _global_registry is None:
|
|
356
|
+
_global_registry = PromptRegistry()
|
|
357
|
+
return _global_registry
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def reset_global_registry() -> None:
|
|
361
|
+
"""Reset the global registry (primarily for testing)."""
|
|
362
|
+
global _global_registry
|
|
363
|
+
_global_registry = None
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# =============================================================================
|
|
367
|
+
# PromptBuilder ABC (Workflow-specific)
|
|
368
|
+
# =============================================================================
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class PromptBuilder(ABC):
|
|
372
|
+
"""
|
|
373
|
+
Abstract base class for workflow-specific prompt builders.
|
|
374
|
+
|
|
375
|
+
Subclasses implement prompt templates for specific consultation workflows,
|
|
376
|
+
providing a consistent interface for prompt generation. Each builder
|
|
377
|
+
manages its own set of templates internally.
|
|
378
|
+
|
|
379
|
+
Methods:
|
|
380
|
+
build: Generate a prompt from a template ID and context
|
|
381
|
+
list_prompts: Return available prompt IDs for this workflow
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
@abstractmethod
|
|
385
|
+
def build(self, prompt_id: str, context: Dict[str, Any]) -> str:
|
|
386
|
+
"""
|
|
387
|
+
Build a prompt from template ID and context.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
prompt_id: Identifier for the prompt template
|
|
391
|
+
context: Structured context data to inject
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
The rendered prompt string
|
|
395
|
+
|
|
396
|
+
Raises:
|
|
397
|
+
ValueError: If prompt_id is not recognized
|
|
398
|
+
"""
|
|
399
|
+
raise NotImplementedError
|
|
400
|
+
|
|
401
|
+
@abstractmethod
|
|
402
|
+
def list_prompts(self) -> List[str]:
|
|
403
|
+
"""
|
|
404
|
+
Return list of available prompt IDs.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
List of prompt template IDs
|
|
408
|
+
"""
|
|
409
|
+
raise NotImplementedError
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
# =============================================================================
|
|
413
|
+
# Workflow Builder Factory
|
|
414
|
+
# =============================================================================
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def get_prompt_builder(workflow: "ConsultationWorkflow") -> PromptBuilder:
|
|
418
|
+
"""
|
|
419
|
+
Get the prompt builder for a consultation workflow.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
workflow: The consultation workflow type
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
PromptBuilder instance for the workflow
|
|
426
|
+
|
|
427
|
+
Raises:
|
|
428
|
+
ValueError: If workflow is not supported
|
|
429
|
+
"""
|
|
430
|
+
# Import here to avoid circular imports
|
|
431
|
+
from foundry_mcp.core.ai_consultation import ConsultationWorkflow
|
|
432
|
+
from foundry_mcp.core.prompts.plan_review import PlanReviewPromptBuilder
|
|
433
|
+
from foundry_mcp.core.prompts.fidelity_review import FidelityReviewPromptBuilder
|
|
434
|
+
from foundry_mcp.core.prompts.markdown_plan_review import MarkdownPlanReviewPromptBuilder
|
|
435
|
+
|
|
436
|
+
builders: Dict[ConsultationWorkflow, type[PromptBuilder]] = {
|
|
437
|
+
ConsultationWorkflow.PLAN_REVIEW: PlanReviewPromptBuilder,
|
|
438
|
+
ConsultationWorkflow.FIDELITY_REVIEW: FidelityReviewPromptBuilder,
|
|
439
|
+
ConsultationWorkflow.MARKDOWN_PLAN_REVIEW: MarkdownPlanReviewPromptBuilder,
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
builder_class = builders.get(workflow)
|
|
443
|
+
if builder_class is None:
|
|
444
|
+
raise ValueError(f"Unsupported workflow: {workflow}")
|
|
445
|
+
|
|
446
|
+
return builder_class()
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# =============================================================================
|
|
450
|
+
# Module Exports
|
|
451
|
+
# =============================================================================
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
__all__ = [
|
|
455
|
+
# Template dataclass
|
|
456
|
+
"PromptTemplate",
|
|
457
|
+
# Registry
|
|
458
|
+
"PromptRegistry",
|
|
459
|
+
"get_global_registry",
|
|
460
|
+
"reset_global_registry",
|
|
461
|
+
# Builder ABC
|
|
462
|
+
"PromptBuilder",
|
|
463
|
+
"get_prompt_builder",
|
|
464
|
+
]
|