Coverage for src/dataknobs_llm/prompts/implementations/config_library.py: 24%
99 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-31 16:04 -0600
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-31 16:04 -0600
1"""Configuration-based prompt library implementation.
3This module provides a prompt library that loads prompts from Python dictionaries.
4Useful for programmatic prompt definition and testing.
6Example:
7 config = {
8 "system": {
9 "analyze_code": {
10 "template": "Analyze this {{language}} code:\\n{{code}}",
11 "defaults": {"language": "python"},
12 "validation": {
13 "level": "error",
14 "required_params": ["code"]
15 }
16 }
17 },
18 "user": {
19 "code_question": {
20 "template": "Please analyze the following code...",
21 "rag_configs": [...]
22 },
23 "followup_question": {
24 "template": "Additionally, check for..."
25 }
26 }
27 }
29 library = ConfigPromptLibrary(config)
30 template = library.get_system_prompt("analyze_code")
31"""
33import logging
34from typing import Any, Dict, List, Optional
36from ..base import (
37 BasePromptLibrary,
38 PromptTemplate,
39 RAGConfig,
40 MessageIndex,
41 ValidationConfig,
42 ValidationLevel,
43)
45logger = logging.getLogger(__name__)
48class ConfigPromptLibrary(BasePromptLibrary):
49 """Prompt library that loads prompts from configuration dictionaries.
51 Features:
52 - Direct in-memory configuration
53 - No filesystem dependencies
54 - Useful for testing and programmatic definition
55 - Supports all prompt types (system, user, messages, RAG)
57 Example:
58 >>> config = {
59 ... "system": {"greet": {"template": "Hello {{name}}!"}},
60 ... "user": {"ask": {"template": "Tell me about {{topic}}"}}
61 ... }
62 >>> library = ConfigPromptLibrary(config)
63 >>> template = library.get_system_prompt("greet")
64 """
66 def __init__(self, config: Optional[Dict[str, Any]] = None):
67 """Initialize configuration-based prompt library.
69 Args:
70 config: Configuration dictionary with structure:
71 {
72 "system": {name: PromptTemplate, ...},
73 "user": {name: PromptTemplate, ...},
74 "messages": {name: MessageIndex, ...},
75 "rag": {name: RAGConfig, ...}
76 }
77 """
78 super().__init__()
79 self._config = config or {}
81 # Load prompts from config
82 self._load_from_config()
84 def _load_from_config(self) -> None:
85 """Load all prompts from the configuration dictionary."""
86 self._load_system_prompts()
87 self._load_user_prompts()
88 self._load_message_indexes()
89 self._load_rag_configs() # Load standalone RAG configs
91 def _load_system_prompts(self) -> None:
92 """Load system prompts from config."""
93 system_config = self._config.get("system", {})
95 for name, data in system_config.items():
96 try:
97 template = self._parse_prompt_template(data)
98 self._cache_system_prompt(name, template)
99 logger.debug(f"Loaded system prompt from config: {name}")
100 except Exception as e:
101 logger.error(f"Error loading system prompt {name}: {e}")
103 def _load_user_prompts(self) -> None:
104 """Load user prompts from config."""
105 user_config = self._config.get("user", {})
107 for name, data in user_config.items():
108 try:
109 template = self._parse_prompt_template(data)
110 self._cache_user_prompt(name, template)
111 logger.debug(f"Loaded user prompt from config: {name}")
112 except Exception as e:
113 logger.error(f"Error loading user prompt {name}: {e}")
115 def _load_message_indexes(self) -> None:
116 """Load message indexes from config."""
117 messages_config = self._config.get("messages", {})
119 for name, data in messages_config.items():
120 try:
121 message_index = self._parse_message_index(data)
122 self._cache_message_index(name, message_index)
123 logger.debug(f"Loaded message index from config: {name}")
124 except Exception as e:
125 logger.error(f"Error loading message index {name}: {e}")
127 def _load_rag_configs(self) -> None:
128 """Load RAG configurations from config."""
129 rag_config = self._config.get("rag", {})
131 for name, data in rag_config.items():
132 try:
133 rag = self._parse_rag_config(data)
134 self._cache_rag_config(name, rag)
135 logger.debug(f"Loaded RAG config from config: {name}")
136 except Exception as e:
137 logger.error(f"Error loading RAG config {name}: {e}")
139 # Note: _parse_prompt_template(), _parse_validation_config(), and
140 # _parse_rag_config() are now inherited from BasePromptLibrary
142 def _parse_message_index(self, data: Dict[str, Any]) -> MessageIndex:
143 """Parse message index from config data.
145 Args:
146 data: Message index data
148 Returns:
149 MessageIndex dictionary
150 """
151 message_index: MessageIndex = {
152 "messages": data.get("messages", []),
153 }
155 # Add optional fields
156 if "rag_configs" in data:
157 message_index["rag_configs"] = [
158 self._parse_rag_config(rag_data)
159 for rag_data in data["rag_configs"]
160 ]
162 if "metadata" in data:
163 message_index["metadata"] = data["metadata"]
165 return message_index
167 def get_system_prompt(self, name: str, **kwargs) -> Optional[PromptTemplate]:
168 """Get a system prompt by name.
170 Args:
171 name: System prompt name
172 **kwargs: Additional arguments (unused)
174 Returns:
175 PromptTemplate if found, None otherwise
176 """
177 return self._get_cached_system_prompt(name)
179 def get_user_prompt(self, name: str, **kwargs) -> Optional[PromptTemplate]:
180 """Get a user prompt by name.
182 Args:
183 name: User prompt name
184 **kwargs: Additional arguments (unused)
186 Returns:
187 PromptTemplate if found, None otherwise
188 """
189 return self._get_cached_user_prompt(name)
191 def get_message_index(self, name: str, **kwargs) -> Optional[MessageIndex]:
192 """Get a message index by name.
194 Args:
195 name: Message index name
196 **kwargs: Additional arguments (unused)
198 Returns:
199 MessageIndex if found, None otherwise
200 """
201 return self._get_cached_message_index(name)
203 def get_rag_config(self, name: str, **kwargs) -> Optional[RAGConfig]:
204 """Get a standalone RAG configuration by name.
206 Args:
207 name: RAG config name
208 **kwargs: Additional arguments (unused)
210 Returns:
211 RAGConfig if found, None otherwise
212 """
213 return self._get_cached_rag_config(name)
215 def get_prompt_rag_configs(
216 self,
217 prompt_name: str,
218 prompt_type: str = "user",
219 **kwargs
220 ) -> List[RAGConfig]:
221 """Get RAG configurations for a specific prompt.
223 Resolves both inline RAG configs and references to standalone configs.
225 Args:
226 prompt_name: Prompt name
227 prompt_type: Type of prompt ("user" or "system")
228 **kwargs: Additional arguments (unused)
230 Returns:
231 List of RAGConfig (empty if none defined)
232 """
233 # Get the prompt template
234 if prompt_type == "system":
235 template = self.get_system_prompt(prompt_name)
236 else:
237 template = self.get_user_prompt(prompt_name)
239 if template is None:
240 return []
242 configs = []
244 # Get inline RAG configs from template
245 if "rag_configs" in template:
246 configs.extend(template["rag_configs"])
248 # Resolve RAG config references
249 if "rag_config_refs" in template:
250 for ref_name in template["rag_config_refs"]:
251 ref_config = self.get_rag_config(ref_name)
252 if ref_config:
253 configs.append(ref_config)
254 else:
255 logger.warning(
256 f"RAG config reference '{ref_name}' not found "
257 f"for prompt '{prompt_name}'"
258 )
260 return configs
262 def add_system_prompt(self, name: str, template: PromptTemplate) -> None:
263 """Add or update a system prompt.
265 Args:
266 name: System prompt name
267 template: Prompt template to add
268 """
269 self._cache_system_prompt(name, template)
270 logger.debug(f"Added/updated system prompt: {name}")
272 def add_user_prompt(self, name: str, template: PromptTemplate) -> None:
273 """Add or update a user prompt.
275 Args:
276 name: User prompt name
277 template: Prompt template to add
278 """
279 self._cache_user_prompt(name, template)
280 logger.debug(f"Added/updated user prompt: {name}")
282 def add_message_index(self, name: str, message_index: MessageIndex) -> None:
283 """Add or update a message index.
285 Args:
286 name: Message index name
287 message_index: Message index to add
288 """
289 self._cache_message_index(name, message_index)
290 logger.debug(f"Added/updated message index: {name}")
292 def add_rag_config(self, name: str, rag_config: RAGConfig) -> None:
293 """Add or update a RAG configuration.
295 Args:
296 name: RAG config name
297 rag_config: RAG configuration to add
298 """
299 self._cache_rag_config(name, rag_config)
300 logger.debug(f"Added/updated RAG config: {name}")
302 def list_system_prompts(self) -> List[str]:
303 """List all available system prompt names.
305 Returns:
306 List of system prompt identifiers
307 """
308 return list(self._system_prompt_cache.keys())
310 def list_user_prompts(self) -> List[str]:
311 """List available user prompts.
313 Returns:
314 List of user prompt names
315 """
316 return list(self._user_prompt_cache.keys())
318 def list_message_indexes(self) -> List[str]:
319 """List all available message index names.
321 Returns:
322 List of message index identifiers
323 """
324 return list(self._message_index_cache.keys())