Update core_mcp.py
Browse files- core_mcp.py +69 -76
core_mcp.py
CHANGED
|
@@ -185,10 +185,6 @@ Generate the complete code based on these clarified requirements."""
|
|
| 185 |
def format_strategist_audit_prompt(blueprint: str, intent: str, generated_code: str) -> str:
|
| 186 |
return f"""{HEADER_PROMPT}
|
| 187 |
|
| 188 |
-
**AUDIT PHASE INITIATED**
|
| 189 |
-
|
| 190 |
-
The Coder AI has generated the following code based on the blueprint. Your task is to AUDIT this code against the original intent and blueprint.
|
| 191 |
-
|
| 192 |
ORIGINAL BLUEPRINT:
|
| 193 |
{blueprint}
|
| 194 |
|
|
@@ -198,22 +194,14 @@ ORIGINAL INTENT:
|
|
| 198 |
GENERATED CODE:
|
| 199 |
{generated_code}
|
| 200 |
|
| 201 |
-
**
|
| 202 |
-
|
| 203 |
-
2. If the code is acceptable and meets all requirements, respond with the exact string: "AUDIT_PASSED".
|
| 204 |
-
3. If changes are required, provide a numbered list of specific, actionable corrections. DO NOT rewrite the code yourself. Just list the required changes.
|
| 205 |
-
"""
|
| 206 |
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
BLUEPRINT:
|
| 211 |
-
{blueprint}
|
| 212 |
|
| 213 |
-
|
| 214 |
-
{audit_feedback}
|
| 215 |
-
|
| 216 |
-
The previous code generation failed the audit. Regenerate the code, strictly adhering to the audit feedback above."""
|
| 217 |
|
| 218 |
# --- UTILITIES ---
|
| 219 |
|
|
@@ -242,6 +230,8 @@ def sanitize_error_message(error_msg: str) -> str:
|
|
| 242 |
Scrub API keys and sensitive URLs from error messages.
|
| 243 |
Removes everything after '?' in URLs to prevent API key exposure.
|
| 244 |
"""
|
|
|
|
|
|
|
| 245 |
# Pattern to match URLs with query parameters (containing API keys)
|
| 246 |
# This will find "for url: https://...?key=..." and remove everything after the ?
|
| 247 |
pattern = r'(for url: https?://[^\s?]+)\?[^\s]*'
|
|
@@ -433,9 +423,7 @@ def execute_http_tool(tool_name: str, tool_config: Dict, params: Dict[str, str],
|
|
| 433 |
|
| 434 |
def call_ai_api(full_url: str, api_key: str, messages: List[Dict[str, str]]) -> str:
|
| 435 |
headers = {'Content-Type': 'application/json'}
|
| 436 |
-
# We construct the URL with the key here for the request...
|
| 437 |
url_with_key = f"{full_url}?key={api_key}"
|
| 438 |
-
|
| 439 |
payload = {
|
| 440 |
'contents': messages,
|
| 441 |
'generationConfig': {
|
|
@@ -445,17 +433,14 @@ def call_ai_api(full_url: str, api_key: str, messages: List[Dict[str, str]]) ->
|
|
| 445 |
}
|
| 446 |
try:
|
| 447 |
response = requests.post(url_with_key, headers=headers, json=payload, timeout=120)
|
| 448 |
-
response.raise_for_status()
|
| 449 |
data = response.json()
|
| 450 |
if 'candidates' in data and len(data['candidates']) > 0:
|
| 451 |
return data['candidates'][0]['content']['parts'][0]['text']
|
| 452 |
else:
|
| 453 |
return f"ERROR: API malformed response: {data}"
|
| 454 |
except Exception as e:
|
| 455 |
-
|
| 456 |
-
# This strips the '?key=xyz' from the error message
|
| 457 |
-
sanitized_error = sanitize_error_message(str(e))
|
| 458 |
-
return f"ERROR: API call failed: {sanitized_error}"
|
| 459 |
|
| 460 |
def resolve_model(selected_label: str, model_registry: Dict[str, Dict], fallback_label: str, agent_name: str) -> Tuple[str, str, str]:
|
| 461 |
if selected_label and selected_label in model_registry:
|
|
@@ -482,6 +467,19 @@ def resolve_model(selected_label: str, model_registry: Dict[str, Dict], fallback
|
|
| 482 |
)
|
| 483 |
return ('', '', f"FATAL: No {agent_name} models configured.")
|
| 484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
# --- ENGINE ---
|
| 486 |
|
| 487 |
class ConductorEngine:
|
|
@@ -491,10 +489,8 @@ class ConductorEngine:
|
|
| 491 |
self.system_log: List[str] = []
|
| 492 |
self.audit_log: List[Dict[str, Any]] = []
|
| 493 |
self.loop_count: int = 0
|
| 494 |
-
self.max_loops: int = 6
|
| 495 |
-
self.enable_audit = enable_audit
|
| 496 |
-
self.final_output_content = ""
|
| 497 |
-
|
| 498 |
self.coder_model_id, self.coder_url, coder_log = resolve_model(
|
| 499 |
coder_model_label, CONFIG['coder_models'], CONFIG['coder_model_default_label'], 'Coder'
|
| 500 |
)
|
|
@@ -522,18 +518,17 @@ class ConductorEngine:
|
|
| 522 |
"coder_log": self.coder_history,
|
| 523 |
"strategist_log": self.strategist_history,
|
| 524 |
"audit_log": self.audit_log,
|
| 525 |
-
"final_output":
|
| 526 |
}
|
| 527 |
|
| 528 |
def execute_workflow(self, blueprint: str, intent: str) -> Dict[str, Any]:
|
| 529 |
"""ORIGINAL: Non-streaming version (kept for backwards compatibility)"""
|
| 530 |
-
# Note: Audit logic not implemented in legacy non-streaming version
|
| 531 |
self.coder_history = []
|
| 532 |
self.strategist_history = []
|
| 533 |
self.system_log = []
|
| 534 |
self.audit_log = []
|
| 535 |
self.loop_count = 0
|
| 536 |
-
|
| 537 |
|
| 538 |
self.log_system("INITIATING CONDUCTOR ENGINE...")
|
| 539 |
for log_msg in self.model_resolution_logs:
|
|
@@ -557,8 +552,8 @@ class ConductorEngine:
|
|
| 557 |
|
| 558 |
if "DETECTED" not in coder_response:
|
| 559 |
self.log_system("MISSION COMPLETE: Ambiguity resolved or Code generated.")
|
| 560 |
-
|
| 561 |
-
return self._compile_results(
|
| 562 |
|
| 563 |
self.log_system("Ambiguity DETECTED. Escalating to Strategist.")
|
| 564 |
strategist_prompt = format_strategist_prompt(blueprint, intent, coder_response)
|
|
@@ -588,13 +583,12 @@ class ConductorEngine:
|
|
| 588 |
return self._compile_results(coder_response)
|
| 589 |
|
| 590 |
def execute_workflow_streaming(self, blueprint: str, intent: str):
|
| 591 |
-
"""
|
| 592 |
self.coder_history = []
|
| 593 |
self.strategist_history = []
|
| 594 |
self.system_log = []
|
| 595 |
self.audit_log = []
|
| 596 |
self.loop_count = 0
|
| 597 |
-
self.final_output_content = ""
|
| 598 |
|
| 599 |
self.log_system("INITIATING CONDUCTOR ENGINE...")
|
| 600 |
yield self._current_state()
|
|
@@ -610,7 +604,7 @@ class ConductorEngine:
|
|
| 610 |
|
| 611 |
self.log_system(f"Engaged: Coder ({self.coder_model_id}), Strategist ({self.strategist_model_id})")
|
| 612 |
if self.enable_audit:
|
| 613 |
-
self.log_system("AUDIT
|
| 614 |
yield self._current_state()
|
| 615 |
|
| 616 |
initial_prompt = format_initial_prompt(blueprint)
|
|
@@ -626,44 +620,44 @@ class ConductorEngine:
|
|
| 626 |
self.log_coder("assistant", coder_response)
|
| 627 |
yield self._current_state()
|
| 628 |
|
| 629 |
-
# CHECK FOR COMPLETION OR AMBIGUITY
|
| 630 |
if "DETECTED" not in coder_response:
|
| 631 |
-
#
|
| 632 |
-
if self.enable_audit:
|
| 633 |
-
|
| 634 |
-
yield self._current_state()
|
| 635 |
-
|
| 636 |
-
audit_prompt = format_strategist_audit_prompt(blueprint, intent, coder_response)
|
| 637 |
-
self.log_strategist("user", audit_prompt)
|
| 638 |
-
yield self._current_state()
|
| 639 |
-
|
| 640 |
-
strategist_response = call_ai_api(self.strategist_url, CONFIG['STRATEGIST_API_KEY'], self.strategist_history)
|
| 641 |
-
self.log_strategist("assistant", strategist_response)
|
| 642 |
-
yield self._current_state()
|
| 643 |
-
|
| 644 |
-
if "AUDIT_PASSED" in strategist_response:
|
| 645 |
-
self.log_system("AUDIT STATUS: PASSED. Mission Complete.")
|
| 646 |
-
self.final_output_content = coder_response
|
| 647 |
-
yield self._current_state()
|
| 648 |
-
return
|
| 649 |
-
else:
|
| 650 |
-
self.log_system("AUDIT STATUS: FAILED. Revisions Requested.")
|
| 651 |
-
yield self._current_state()
|
| 652 |
-
|
| 653 |
-
# Feed audit feedback back to Coder
|
| 654 |
-
audit_resolution_prompt = format_audit_resolution_prompt(blueprint, strategist_response)
|
| 655 |
-
self.log_coder("user", audit_resolution_prompt)
|
| 656 |
-
yield self._current_state()
|
| 657 |
-
# Loop continues to next iteration
|
| 658 |
-
continue
|
| 659 |
-
else:
|
| 660 |
-
# No audit, just finish
|
| 661 |
self.log_system("MISSION COMPLETE: Ambiguity resolved or Code generated.")
|
| 662 |
-
|
| 663 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 664 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
|
| 666 |
-
# AMBIGUITY DETECTED PATH
|
| 667 |
self.log_system("Ambiguity DETECTED. Escalating to Strategist.")
|
| 668 |
yield self._current_state()
|
| 669 |
|
|
@@ -699,7 +693,6 @@ class ConductorEngine:
|
|
| 699 |
yield self._current_state()
|
| 700 |
|
| 701 |
self.log_system("FATAL: Loop limit reached.")
|
| 702 |
-
self.final_output_content = coder_response # Return whatever we have
|
| 703 |
yield self._current_state()
|
| 704 |
|
| 705 |
def _compile_results(self, final_output: str) -> Dict[str, Any]:
|
|
@@ -713,14 +706,14 @@ class ConductorEngine:
|
|
| 713 |
|
| 714 |
# --- API ---
|
| 715 |
|
| 716 |
-
app = FastAPI(title="Conductor MCP Server", version="4.
|
| 717 |
|
| 718 |
class ExecutionRequest(BaseModel):
|
| 719 |
blueprint: str
|
| 720 |
intent: str
|
| 721 |
coder_model_label: Optional[str] = None
|
| 722 |
strategist_model_label: Optional[str] = None
|
| 723 |
-
enable_audit: Optional[bool] = False
|
| 724 |
|
| 725 |
class ExecutionResponse(BaseModel):
|
| 726 |
system_log: List[str]
|
|
@@ -738,7 +731,7 @@ async def execute_conductor(req: ExecutionRequest):
|
|
| 738 |
|
| 739 |
@app.post("/execute_stream")
|
| 740 |
async def execute_conductor_stream(req: ExecutionRequest):
|
| 741 |
-
"""
|
| 742 |
|
| 743 |
async def generate_updates():
|
| 744 |
engine = ConductorEngine(req.coder_model_label, req.strategist_model_label, req.enable_audit)
|
|
@@ -752,4 +745,4 @@ async def execute_conductor_stream(req: ExecutionRequest):
|
|
| 752 |
return StreamingResponse(generate_updates(), media_type="text/event-stream")
|
| 753 |
|
| 754 |
if __name__ == "__main__":
|
| 755 |
-
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
|
| 185 |
def format_strategist_audit_prompt(blueprint: str, intent: str, generated_code: str) -> str:
|
| 186 |
return f"""{HEADER_PROMPT}
|
| 187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
ORIGINAL BLUEPRINT:
|
| 189 |
{blueprint}
|
| 190 |
|
|
|
|
| 194 |
GENERATED CODE:
|
| 195 |
{generated_code}
|
| 196 |
|
| 197 |
+
**[AUDIT DIRECTIVE]**
|
| 198 |
+
Review the generated code for compliance with the blueprint and intent.
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
+
Respond with EXACTLY ONE of the following:
|
| 201 |
+
1. If code is acceptable: "AUDIT_PASSED"
|
| 202 |
+
2. If changes required: Provide a numbered list of required changes.
|
|
|
|
|
|
|
| 203 |
|
| 204 |
+
Do not generate code. Only provide audit results."""
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
# --- UTILITIES ---
|
| 207 |
|
|
|
|
| 230 |
Scrub API keys and sensitive URLs from error messages.
|
| 231 |
Removes everything after '?' in URLs to prevent API key exposure.
|
| 232 |
"""
|
| 233 |
+
import re
|
| 234 |
+
|
| 235 |
# Pattern to match URLs with query parameters (containing API keys)
|
| 236 |
# This will find "for url: https://...?key=..." and remove everything after the ?
|
| 237 |
pattern = r'(for url: https?://[^\s?]+)\?[^\s]*'
|
|
|
|
| 423 |
|
| 424 |
def call_ai_api(full_url: str, api_key: str, messages: List[Dict[str, str]]) -> str:
|
| 425 |
headers = {'Content-Type': 'application/json'}
|
|
|
|
| 426 |
url_with_key = f"{full_url}?key={api_key}"
|
|
|
|
| 427 |
payload = {
|
| 428 |
'contents': messages,
|
| 429 |
'generationConfig': {
|
|
|
|
| 433 |
}
|
| 434 |
try:
|
| 435 |
response = requests.post(url_with_key, headers=headers, json=payload, timeout=120)
|
| 436 |
+
response.raise_for_status()
|
| 437 |
data = response.json()
|
| 438 |
if 'candidates' in data and len(data['candidates']) > 0:
|
| 439 |
return data['candidates'][0]['content']['parts'][0]['text']
|
| 440 |
else:
|
| 441 |
return f"ERROR: API malformed response: {data}"
|
| 442 |
except Exception as e:
|
| 443 |
+
return f"ERROR: API call failed: {str(e)}"
|
|
|
|
|
|
|
|
|
|
| 444 |
|
| 445 |
def resolve_model(selected_label: str, model_registry: Dict[str, Dict], fallback_label: str, agent_name: str) -> Tuple[str, str, str]:
|
| 446 |
if selected_label and selected_label in model_registry:
|
|
|
|
| 467 |
)
|
| 468 |
return ('', '', f"FATAL: No {agent_name} models configured.")
|
| 469 |
|
| 470 |
+
def format_audit_entry(diagnostic: Dict[str, any]) -> str:
|
| 471 |
+
entry = {
|
| 472 |
+
"transaction_id": diagnostic['transaction_id'],
|
| 473 |
+
"timestamp": diagnostic['timestamp'],
|
| 474 |
+
"tool": diagnostic['tool_name'],
|
| 475 |
+
"status": diagnostic['status'],
|
| 476 |
+
"latency": diagnostic['latency'],
|
| 477 |
+
"request": diagnostic['request_payload'],
|
| 478 |
+
"response": diagnostic['response_payload'],
|
| 479 |
+
"error": diagnostic['error_message']
|
| 480 |
+
}
|
| 481 |
+
return json.dumps(entry, indent=2)
|
| 482 |
+
|
| 483 |
# --- ENGINE ---
|
| 484 |
|
| 485 |
class ConductorEngine:
|
|
|
|
| 489 |
self.system_log: List[str] = []
|
| 490 |
self.audit_log: List[Dict[str, Any]] = []
|
| 491 |
self.loop_count: int = 0
|
| 492 |
+
self.max_loops: int = 6
|
| 493 |
+
self.enable_audit: bool = enable_audit
|
|
|
|
|
|
|
| 494 |
self.coder_model_id, self.coder_url, coder_log = resolve_model(
|
| 495 |
coder_model_label, CONFIG['coder_models'], CONFIG['coder_model_default_label'], 'Coder'
|
| 496 |
)
|
|
|
|
| 518 |
"coder_log": self.coder_history,
|
| 519 |
"strategist_log": self.strategist_history,
|
| 520 |
"audit_log": self.audit_log,
|
| 521 |
+
"final_output": ""
|
| 522 |
}
|
| 523 |
|
| 524 |
def execute_workflow(self, blueprint: str, intent: str) -> Dict[str, Any]:
|
| 525 |
"""ORIGINAL: Non-streaming version (kept for backwards compatibility)"""
|
|
|
|
| 526 |
self.coder_history = []
|
| 527 |
self.strategist_history = []
|
| 528 |
self.system_log = []
|
| 529 |
self.audit_log = []
|
| 530 |
self.loop_count = 0
|
| 531 |
+
final_code = ""
|
| 532 |
|
| 533 |
self.log_system("INITIATING CONDUCTOR ENGINE...")
|
| 534 |
for log_msg in self.model_resolution_logs:
|
|
|
|
| 552 |
|
| 553 |
if "DETECTED" not in coder_response:
|
| 554 |
self.log_system("MISSION COMPLETE: Ambiguity resolved or Code generated.")
|
| 555 |
+
final_code = coder_response
|
| 556 |
+
return self._compile_results(final_code)
|
| 557 |
|
| 558 |
self.log_system("Ambiguity DETECTED. Escalating to Strategist.")
|
| 559 |
strategist_prompt = format_strategist_prompt(blueprint, intent, coder_response)
|
|
|
|
| 583 |
return self._compile_results(coder_response)
|
| 584 |
|
| 585 |
def execute_workflow_streaming(self, blueprint: str, intent: str):
|
| 586 |
+
"""STREAMING version with TASK 3 & 4 modifications"""
|
| 587 |
self.coder_history = []
|
| 588 |
self.strategist_history = []
|
| 589 |
self.system_log = []
|
| 590 |
self.audit_log = []
|
| 591 |
self.loop_count = 0
|
|
|
|
| 592 |
|
| 593 |
self.log_system("INITIATING CONDUCTOR ENGINE...")
|
| 594 |
yield self._current_state()
|
|
|
|
| 604 |
|
| 605 |
self.log_system(f"Engaged: Coder ({self.coder_model_id}), Strategist ({self.strategist_model_id})")
|
| 606 |
if self.enable_audit:
|
| 607 |
+
self.log_system("AUDIT MODE: ENABLED")
|
| 608 |
yield self._current_state()
|
| 609 |
|
| 610 |
initial_prompt = format_initial_prompt(blueprint)
|
|
|
|
| 620 |
self.log_coder("assistant", coder_response)
|
| 621 |
yield self._current_state()
|
| 622 |
|
|
|
|
| 623 |
if "DETECTED" not in coder_response:
|
| 624 |
+
# TASK 4: AUDIT LOOP LOGIC
|
| 625 |
+
if not self.enable_audit:
|
| 626 |
+
# Original termination path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 627 |
self.log_system("MISSION COMPLETE: Ambiguity resolved or Code generated.")
|
| 628 |
+
final_state = self._current_state()
|
| 629 |
+
final_state["final_code"] = coder_response # TASK 3: Add final_code key
|
| 630 |
+
yield final_state
|
| 631 |
+
return
|
| 632 |
+
# AUDIT MODE ACTIVATED
|
| 633 |
+
self.log_system("AUDIT PHASE INITIATED.")
|
| 634 |
+
yield self._current_state()
|
| 635 |
+
|
| 636 |
+
audit_prompt = format_strategist_audit_prompt(blueprint, intent, coder_response)
|
| 637 |
+
self.log_strategist("user", audit_prompt)
|
| 638 |
+
yield self._current_state()
|
| 639 |
+
|
| 640 |
+
audit_response = call_ai_api(self.strategist_url, CONFIG['STRATEGIST_API_KEY'], self.strategist_history)
|
| 641 |
+
self.log_strategist("assistant", audit_response)
|
| 642 |
+
yield self._current_state()
|
| 643 |
+
|
| 644 |
+
if "AUDIT_PASSED" in audit_response:
|
| 645 |
+
self.log_system("AUDIT PASSED. MISSION COMPLETE.")
|
| 646 |
+
final_state = self._current_state()
|
| 647 |
+
final_state["final_code"] = coder_response # TASK 3: Add final_code key
|
| 648 |
+
yield final_state
|
| 649 |
return
|
| 650 |
+
|
| 651 |
+
# Audit failed - loop back to coder
|
| 652 |
+
self.log_system("AUDIT FAILED. Generating revisions.")
|
| 653 |
+
yield self._current_state()
|
| 654 |
+
|
| 655 |
+
revision_prompt = format_resolution_prompt(blueprint, f"AUDIT CHANGES REQUIRED:\n{audit_response}")
|
| 656 |
+
self.log_coder("user", revision_prompt)
|
| 657 |
+
yield self._current_state()
|
| 658 |
+
# Continue loop for coder to process revisions
|
| 659 |
+
continue
|
| 660 |
|
|
|
|
| 661 |
self.log_system("Ambiguity DETECTED. Escalating to Strategist.")
|
| 662 |
yield self._current_state()
|
| 663 |
|
|
|
|
| 693 |
yield self._current_state()
|
| 694 |
|
| 695 |
self.log_system("FATAL: Loop limit reached.")
|
|
|
|
| 696 |
yield self._current_state()
|
| 697 |
|
| 698 |
def _compile_results(self, final_output: str) -> Dict[str, Any]:
|
|
|
|
| 706 |
|
| 707 |
# --- API ---
|
| 708 |
|
| 709 |
+
app = FastAPI(title="Conductor MCP Server", version="4.0")
|
| 710 |
|
| 711 |
class ExecutionRequest(BaseModel):
|
| 712 |
blueprint: str
|
| 713 |
intent: str
|
| 714 |
coder_model_label: Optional[str] = None
|
| 715 |
strategist_model_label: Optional[str] = None
|
| 716 |
+
enable_audit: Optional[bool] = False # TASK 4: New field
|
| 717 |
|
| 718 |
class ExecutionResponse(BaseModel):
|
| 719 |
system_log: List[str]
|
|
|
|
| 731 |
|
| 732 |
@app.post("/execute_stream")
|
| 733 |
async def execute_conductor_stream(req: ExecutionRequest):
|
| 734 |
+
"""STREAMING endpoint with TASK 4 audit support"""
|
| 735 |
|
| 736 |
async def generate_updates():
|
| 737 |
engine = ConductorEngine(req.coder_model_label, req.strategist_model_label, req.enable_audit)
|
|
|
|
| 745 |
return StreamingResponse(generate_updates(), media_type="text/event-stream")
|
| 746 |
|
| 747 |
if __name__ == "__main__":
|
| 748 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|