@@ -38,35 +38,22 @@ class AmbiguityResolver:
3838 def __init__ (
3939 self ,
4040 model : Optional [Model ] = None ,
41- model_registry : Optional [ModelRegistry ] = None ,
42- fallback_to_heuristics : bool = False
41+ model_registry : Optional [ModelRegistry ] = None
4342 ) -> None :
4443 """
4544 Initialize ambiguity resolver.
4645
4746 Args:
4847 model: Specific model to use for resolution
4948 model_registry: Model registry to select models from
50- fallback_to_heuristics: Whether to fall back to heuristics if AI fails
5149
5250 Raises:
5351 ValueError: If no model is provided and registry has no models
5452 """
5553 self .model = model
5654 self .model_registry = model_registry
57- self .fallback_to_heuristics = fallback_to_heuristics
5855 self .resolution_cache : Dict [str , Any ] = {}
5956
60- # Heuristic strategies for fallback
61- self .resolution_strategies = {
62- "parameter" : self ._resolve_parameter_heuristic ,
63- "value" : self ._resolve_value_heuristic ,
64- "list" : self ._resolve_list_heuristic ,
65- "boolean" : self ._resolve_boolean_heuristic ,
66- "number" : self ._resolve_number_heuristic ,
67- "string" : self ._resolve_string_heuristic ,
68- }
69-
7057 # If no model provided, try to get one from registry
7158 if not model and model_registry :
7259 available_models = model_registry .list_models ()
@@ -79,10 +66,10 @@ def __init__(
7966 self .model = model_registry .get_model (available_models [0 ])
8067
8168 # Verify we have a model for AI resolution
82- if not self .model and not self . fallback_to_heuristics :
69+ if not self .model :
8370 raise ValueError (
8471 "No AI model available for ambiguity resolution. "
85- "Either provide a model or enable fallback_to_heuristics ."
72+ "A real model must be provided ."
8673 )
8774
8875 async def resolve (self , content : str , context_path : str ) -> Any :
@@ -105,31 +92,19 @@ async def resolve(self, content: str, context_path: str) -> Any:
10592 return self .resolution_cache [cache_key ]
10693
10794 try :
108- # Try AI resolution first if we have a model
95+ # Try AI resolution with the model
10996 if self .model :
11097 result = await self ._resolve_with_ai (content , context_path )
111- elif self .fallback_to_heuristics :
112- # Fall back to heuristic resolution
113- result = await self ._resolve_with_heuristics (content , context_path )
11498 else :
11599 raise AmbiguityResolutionError (
116- "No AI model available and heuristic fallback is disabled "
100+ "No AI model available for ambiguity resolution "
117101 )
118102
119103 # Cache result
120104 self .resolution_cache [cache_key ] = result
121105 return result
122106
123107 except Exception as e :
124- if self .fallback_to_heuristics and self .model :
125- # If AI failed, try heuristics
126- try :
127- result = await self ._resolve_with_heuristics (content , context_path )
128- self .resolution_cache [cache_key ] = result
129- return result
130- except :
131- pass
132-
133108 raise AmbiguityResolutionError (
134109 f"Failed to resolve ambiguity '{ content } ' at { context_path } : { e } "
135110 ) from e
@@ -252,188 +227,6 @@ def _parse_ai_response(self, response: str, expected_type: str, original_content
252227 # String - clean up the response
253228 return response .strip ('"\' ' )
254229
255- async def _resolve_with_heuristics (self , content : str , context_path : str ) -> Any :
256- """Fall back to heuristic resolution."""
257- # Classify ambiguity type
258- resolution_type = self ._classify_ambiguity (content , context_path )
259-
260- # Apply resolution strategy
261- if resolution_type in self .resolution_strategies :
262- strategy = self .resolution_strategies [resolution_type ]
263- return await strategy (content , context_path )
264- else :
265- # Default to string
266- return content
267-
268- def _classify_ambiguity (self , content : str , context_path : str ) -> str :
269- """Classify the type of ambiguity for heuristic resolution."""
270- content_lower = content .lower ()
271-
272- # Check for explicit choice patterns
273- if ("choose" in content_lower or "select" in content_lower ):
274- if any (word in content_lower for word in ["true" , "false" ]):
275- return "boolean"
276- elif any (word in content_lower for word in ["number" , "size" , "count" , "amount" ]):
277- return "number"
278- elif "list" in content_lower or "array" in content_lower :
279- return "list"
280- else :
281- return "value"
282-
283- # Check for parameter context
284- if "parameters" in context_path :
285- return "parameter"
286-
287- # Check for specific patterns
288- if re .search (r'"[^"]*"' , content ):
289- return "string"
290-
291- # Context-based classification
292- path_parts = context_path .lower ().split ('.' )
293- if any (part in ["format" , "type" , "style" , "method" ] for part in path_parts ):
294- return "string"
295- elif any (part in ["enable" , "disable" , "support" ] for part in path_parts ):
296- return "boolean"
297- elif any (part in ["count" , "size" , "limit" , "timeout" , "batch" ] for part in path_parts ):
298- return "number"
299- elif any (part in ["languages" , "formats" , "items" , "sources" , "tags" , "options" , "list" ] for part in path_parts ):
300- return "list"
301-
302- return "string"
303-
304- # Heuristic resolution methods
305- async def _resolve_parameter_heuristic (self , content : str , context_path : str ) -> Any :
306- """Heuristic parameter resolution."""
307- # Check context for common patterns
308- if "format" in context_path :
309- return "json"
310- elif "method" in context_path :
311- return "default"
312- elif "type" in context_path :
313- return "auto"
314- return "default"
315-
316- async def _resolve_value_heuristic (self , content : str , context_path : str ) -> Any :
317- """Heuristic value resolution."""
318- # Extract choices if present
319- choices = self ._extract_choices (content )
320- if choices :
321- return choices [0 ]
322-
323- # Check for specific patterns
324- if "format" in context_path :
325- return "json"
326- elif "method" in context_path :
327- return "auto"
328- elif "style" in context_path :
329- return "default"
330-
331- return "default"
332-
333- async def _resolve_list_heuristic (self , content : str , context_path : str ) -> List [str ]:
334- """Heuristic list resolution."""
335- # Context-based defaults
336- if "source" in context_path :
337- return ["web" , "documents" , "database" ]
338- elif "format" in context_path :
339- return ["json" , "csv" , "xml" ]
340- elif "language" in context_path :
341- return ["en" , "es" , "fr" ]
342- return ["item1" , "item2" , "item3" ]
343-
344- async def _resolve_boolean_heuristic (self , content : str , context_path : str ) -> bool :
345- """Heuristic boolean resolution."""
346- content_lower = content .lower ()
347-
348- # Check for explicit indicators
349- positive = ["enable" , "true" , "yes" , "allow" , "support" ]
350- negative = ["disable" , "false" , "no" , "deny" , "block" ]
351-
352- for word in positive :
353- if word in content_lower :
354- return True
355-
356- for word in negative :
357- if word in content_lower :
358- return False
359-
360- # Context-based defaults
361- if "enable" in context_path or "support" in context_path :
362- return True
363-
364- return False
365-
366- async def _resolve_number_heuristic (self , content : str , context_path : str ) -> Union [int , float ]:
367- """Heuristic number resolution."""
368- # Context-based defaults
369- if "batch" in context_path :
370- return 32
371- elif "timeout" in context_path :
372- return 30
373- elif "retry" in context_path :
374- return 3
375- elif "size" in context_path :
376- return 100
377- elif "limit" in context_path :
378- return 1000
379- return 10
380-
381- async def _resolve_string_heuristic (self , content : str , context_path : str ) -> str :
382- """Heuristic string resolution."""
383- # Extract quoted content
384- quotes = self ._extract_quotes (content )
385- if quotes :
386- return quotes [0 ]
387-
388- # Context-based defaults
389- if "query" in context_path :
390- return "default search query"
391- elif "format" in context_path :
392- return "json"
393- elif "method" in context_path :
394- return "auto"
395- elif "style" in context_path :
396- return "default"
397- elif "type" in context_path :
398- return "standard"
399-
400- return "default"
401-
402- def _extract_choices (self , content : str ) -> List [str ]:
403- """Extract choices from content."""
404- # Pattern for "Choose: A, B, C" or "Select from: A, B, C"
405- pattern = r'(?:choose|select)(?:\s+from)?:\s*([^.]+)'
406- match = re .search (pattern , content , re .IGNORECASE )
407-
408- if match :
409- choices_text = match .group (1 )
410- # Split by comma and clean
411- choices = [c .strip () for c in choices_text .split (',' )]
412- # Remove 'or' from choices
413- choices = [c .replace (' or ' , '' ).replace ('or ' , '' ).strip () for c in choices ]
414- return [c for c in choices if c ]
415-
416- return []
417-
418- def _extract_quotes (self , content : str ) -> List [str ]:
419- """Extract quoted strings from content."""
420- pattern = r'"([^"]*)"'
421- return re .findall (pattern , content )
422-
423- def _fallback_resolution (self , content : str , context_path : str ) -> Any :
424- """Ultimate fallback resolution."""
425- # This is used by tests that expect specific behavior
426- if "query" in context_path :
427- return "default search query"
428- elif "format" in context_path :
429- return "json"
430- elif "method" in context_path :
431- return "auto"
432- elif "style" in context_path :
433- return "default"
434- elif "type" in context_path :
435- return "standard"
436- return "default"
437230
438231 def clear_cache (self ) -> None :
439232 """Clear the resolution cache."""
0 commit comments