@@ -20,12 +20,16 @@ class HealConfig:
2020 judge_model: Model id for the Gate 2 judge (uses DeepSeek/OpenAI keys from env).
2121 repair_model: Optional override model for repair calls. ``None`` reuses the
2222 same model that produced the failing output.
23+ meta_prompt_enabled: When True, generate a task-specific system prompt via
24+ a cheap LLM before each heal step. Combined with repair prompts on retry.
25+ Fails open (never blocks or raises).
2326 """
2427
2528 max_retries : int = 2
2629 gate2_enabled : bool = False
2730 judge_model : str = "deepseek-chat"
2831 repair_model : Optional [str ] = None
32+ meta_prompt_enabled : bool = False
2933
3034from kalibr .provision import resolve_credentials
3135from opentelemetry import trace as otel_trace
@@ -39,6 +43,11 @@ class HealConfig:
3943_auto_reported_traces : set = set ()
4044_auto_reported_lock = threading .Lock ()
4145
46+ # Module-level cache for generated meta-prompts. Key: hash of goal + user preview.
47+ # Value: (generated_prompt, timestamp). TTL enforced at read time.
48+ _meta_prompt_cache : Dict [str , tuple ] = {}
49+ _META_PROMPT_TTL_SECONDS = 300.0
50+
4251
4352def _auto_report_outcome (
4453 trace_id : str ,
@@ -255,6 +264,87 @@ def _gate2_judge(
255264 pass
256265 return {"score" : None , "issues" : [], "skipped" : True }
257266
267+ async def _generate_meta_prompt (
268+ self ,
269+ goal : str ,
270+ messages : list ,
271+ model_id : str ,
272+ ) -> Optional [str ]:
273+ """Generate a task-specific system prompt via a cheap LLM. Never raises.
274+
275+ Returns None on any error (missing key, parse failure, network error).
276+ Results cached for 5 minutes keyed on goal + first 100 chars of last user
277+ message, so repeated identical requests reuse the same prompt.
278+ """
279+ import asyncio as _asyncio
280+ import hashlib as _hashlib
281+ import time as _time
282+
283+ try :
284+ user_preview = ""
285+ if messages :
286+ last = messages [- 1 ]
287+ if isinstance (last , dict ) and last .get ("role" ) == "user" :
288+ user_preview = str (last .get ("content" ) or "" )
289+ else :
290+ for m in reversed (messages ):
291+ if isinstance (m , dict ) and m .get ("role" ) == "user" :
292+ user_preview = str (m .get ("content" ) or "" )
293+ break
294+
295+ cache_key_raw = f"{ goal } |{ user_preview [:100 ]} "
296+ cache_key = _hashlib .sha256 (cache_key_raw .encode ("utf-8" )).hexdigest ()
297+
298+ now = _time .time ()
299+ cached = _meta_prompt_cache .get (cache_key )
300+ if cached is not None :
301+ prompt , ts = cached
302+ if now - ts < _META_PROMPT_TTL_SECONDS :
303+ return prompt
304+
305+ deepseek_key = os .environ .get ("DEEPSEEK_API_KEY" )
306+ openai_key = os .environ .get ("OPENAI_API_KEY" )
307+ if not deepseek_key and not openai_key :
308+ return None
309+
310+ try :
311+ from openai import OpenAI
312+ except ImportError :
313+ return None
314+
315+ meta_prompt = (
316+ "Generate a concise system prompt (under 150 words) for an AI completing this task.\n "
317+ f"Goal type: { goal } \n "
318+ f"Task preview: { user_preview [:300 ]} \n "
319+ "Output ONLY the system prompt text."
320+ )
321+
322+ def _do_call () -> Optional [str ]:
323+ try :
324+ if deepseek_key :
325+ client = OpenAI (api_key = deepseek_key , base_url = "https://api.deepseek.com" )
326+ model = "deepseek-chat"
327+ else :
328+ client = OpenAI (api_key = openai_key )
329+ model = "gpt-4o-mini"
330+ response = client .chat .completions .create (
331+ model = model ,
332+ messages = [{"role" : "user" , "content" : meta_prompt }],
333+ timeout = 15.0 ,
334+ )
335+ return (response .choices [0 ].message .content or "" ).strip ()
336+ except Exception :
337+ return None
338+
339+ generated = await _asyncio .to_thread (_do_call )
340+ if not generated :
341+ return None
342+
343+ _meta_prompt_cache [cache_key ] = (generated , now )
344+ return generated
345+ except Exception :
346+ return None
347+
258348 def _repair_prompt (
259349 self ,
260350 goal : str ,
@@ -302,6 +392,7 @@ def run(
302392 max_retries : int = 2 ,
303393 gate2_enabled : bool = False ,
304394 judge_model : str = "deepseek-chat" ,
395+ meta_prompt_enabled : bool = False ,
305396 ) -> Dict [str , Any ]:
306397 """Run the heal loop across paths until success or exhaustion.
307398
@@ -313,14 +404,28 @@ def run(
313404 last_failure_category : Optional [str ] = None
314405 last_response : Any = None
315406
407+ meta_sys_prompt : Optional [str ] = None
408+ if meta_prompt_enabled :
409+ import asyncio as _asyncio
410+ first_model = ""
411+ if paths :
412+ first_path = paths [0 ]
413+ first_model = first_path ["model" ] if isinstance (first_path , dict ) else str (first_path )
414+ try :
415+ meta_sys_prompt = _asyncio .run (
416+ self ._generate_meta_prompt (goal , messages , first_model )
417+ )
418+ except Exception :
419+ meta_sys_prompt = None
420+
316421 for path in paths :
317422 model_id = path ["model" ] if isinstance (path , dict ) else str (path )
318423 if not model_id :
319424 continue
320425 if model_id not in models_tried :
321426 models_tried .append (model_id )
322427
323- repair_system : Optional [str ] = None
428+ repair_system : Optional [str ] = meta_sys_prompt
324429
325430 for attempt in range (max_retries + 1 ):
326431 output = ""
@@ -372,7 +477,7 @@ def run(
372477 break
373478 if gate2_issues :
374479 repair = f"{ repair } Gate 2 judge flagged: { gate2_issues } "
375- repair_system = repair
480+ repair_system = f" { meta_sys_prompt } \n \n { repair } " if meta_sys_prompt else repair
376481 heal_count += 1
377482 continue
378483
@@ -388,7 +493,7 @@ def run(
388493 repair = self ._repair_prompt (goal , output , messages , model_id )
389494 if not repair :
390495 break
391- repair_system = repair
496+ repair_system = f" { meta_sys_prompt } \n \n { repair } " if meta_sys_prompt else repair
392497 heal_count += 1
393498
394499 return {
@@ -737,6 +842,7 @@ def _heal_dispatch(m_id: str, msgs: List[Dict], system_prompt: Optional[str] = N
737842 max_retries = cfg .max_retries ,
738843 gate2_enabled = cfg .gate2_enabled ,
739844 judge_model = cfg .judge_model ,
845+ meta_prompt_enabled = cfg .meta_prompt_enabled ,
740846 )
741847
742848 router_span .set_attribute ("kalibr.healing" , True )
0 commit comments