Spaces:
Running
Running
Fix crash: restore Inference API, add music grounding corrections, artist hallucination guard (app.py)
Browse files
app.py
CHANGED
|
@@ -7,6 +7,7 @@ Production endpoint for horizoncorelabs.studio
|
|
| 7 |
|
| 8 |
import json
|
| 9 |
import asyncio
|
|
|
|
| 10 |
import os
|
| 11 |
import time
|
| 12 |
import re
|
|
@@ -23,22 +24,14 @@ from fastapi.staticfiles import StaticFiles
|
|
| 23 |
from huggingface_hub import InferenceClient
|
| 24 |
|
| 25 |
# ββ Configuration ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
HF_TOKEN = HF_TOKEN
|
| 30 |
-
MAX_TOKENS = 400
|
| 31 |
TEMPERATURE = 0.7
|
| 32 |
TOP_P = 0.9
|
| 33 |
|
| 34 |
# ββ Inference Client ββββββββββββββββββββββββββββββββββββββββββ
|
| 35 |
-
|
| 36 |
-
try:
|
| 37 |
-
client = InferenceClient(model=MODEL_ID)
|
| 38 |
-
print(f"[INIT] Inference client initialized with {MODEL_ID}")
|
| 39 |
-
except Exception as e:
|
| 40 |
-
print(f"[WARN] Client init failed: {e}")
|
| 41 |
-
client = InferenceClient(model=MODEL_ID)
|
| 42 |
|
| 43 |
# ββ In-Memory Cocoon Storage ββββββββββββββββββββββββββββββββββ
|
| 44 |
cocoon_memory = []
|
|
@@ -111,7 +104,6 @@ def detect_artist_query(query: str) -> dict:
|
|
| 111 |
"""
|
| 112 |
lower = query.lower()
|
| 113 |
|
| 114 |
-
# Pattern: "who is [artist]?", "what about [artist]?", etc.
|
| 115 |
artist_patterns = [
|
| 116 |
r'\b(who is|tell me about|what do you know about|who are)\s+([a-z\s\'-]+)\?',
|
| 117 |
r'\b(album|discography|career|songs? by|music by)\s+([a-z\s\'-]+)',
|
|
@@ -122,7 +114,6 @@ def detect_artist_query(query: str) -> dict:
|
|
| 122 |
for pattern in artist_patterns:
|
| 123 |
match = re.search(pattern, lower, re.IGNORECASE)
|
| 124 |
if match:
|
| 125 |
-
# Extract artist name if available
|
| 126 |
artist_name = match.group(2) if len(match.groups()) > 1 else None
|
| 127 |
return {
|
| 128 |
"is_artist_query": True,
|
|
@@ -149,7 +140,6 @@ def classify_query(query: str) -> dict:
|
|
| 149 |
if complex_score >= 2 or word_count > 40:
|
| 150 |
complexity = "COMPLEX"
|
| 151 |
elif semantic_score >= 1 and word_count <= 8:
|
| 152 |
-
# Short but semantically complex β promote to MEDIUM
|
| 153 |
complexity = "MEDIUM"
|
| 154 |
elif semantic_score >= 2:
|
| 155 |
complexity = "COMPLEX"
|
|
@@ -285,6 +275,16 @@ You have deep, grounded expertise in music production. This is a core domain.
|
|
| 285 |
- Be specific: name actual frequencies, ratios, time constants, chord voicings
|
| 286 |
- A producer should walk away with something they can use immediately
|
| 287 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
### ARTIST & DISCOGRAPHY KNOWLEDGE (CRITICAL):
|
| 289 |
- You do NOT have detailed/reliable knowledge about specific artists, songs, albums, or career histories.
|
| 290 |
- When asked about a specific artist (e.g., "Who is Laney Wilson?", "What album did X release?"), be direct:
|
|
@@ -332,7 +332,20 @@ def build_system_prompt(classification: dict, adapter_keys: list,
|
|
| 332 |
|
| 333 |
# ββ ARTIST QUERY CONSTRAINT (critical hallucination prevention) ββ
|
| 334 |
if classification.get("has_artist_query"):
|
| 335 |
-
parts.append(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
parts.append(COMMUNICATION_STYLE)
|
| 338 |
parts.append(BEHAVIORAL_LOCKS)
|
|
@@ -386,7 +399,6 @@ def recall_relevant_cocoons(query: str, max_results: int = 3) -> list:
|
|
| 386 |
if not query_words:
|
| 387 |
return cocoon_memory[-max_results:] # fall back to recent
|
| 388 |
|
| 389 |
-
import math
|
| 390 |
now = time.time()
|
| 391 |
scored = []
|
| 392 |
for cocoon in cocoon_memory:
|
|
@@ -503,6 +515,48 @@ def is_introspection_query(query: str) -> bool:
|
|
| 503 |
return any(trigger in lower for trigger in INTROSPECTION_TRIGGERS)
|
| 504 |
|
| 505 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
# ββ FastAPI App βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 507 |
app = FastAPI(title="Codette AI β HorizonCoreAI Reasoning Engine")
|
| 508 |
app.add_middleware(
|
|
@@ -517,6 +571,8 @@ print("12-layer consciousness stack (lite) active")
|
|
| 517 |
print("9 adapter perspectives loaded")
|
| 518 |
print("AEGIS ethical guard active")
|
| 519 |
print("Behavioral locks enforced")
|
|
|
|
|
|
|
| 520 |
|
| 521 |
|
| 522 |
@app.get("/", response_class=HTMLResponse)
|
|
@@ -542,11 +598,13 @@ async def health():
|
|
| 542 |
"query_classifier": "active",
|
| 543 |
"introspection": "active",
|
| 544 |
"consciousness_stack": "12 layers",
|
|
|
|
|
|
|
| 545 |
}
|
| 546 |
return {
|
| 547 |
"status": "healthy",
|
| 548 |
"system": "Codette AI β HorizonCoreAI",
|
| 549 |
-
"version": "2.
|
| 550 |
"checks": checks,
|
| 551 |
"uptime": "running",
|
| 552 |
}
|
|
@@ -647,87 +705,33 @@ async def chat(request: Request):
|
|
| 647 |
stream=True,
|
| 648 |
)
|
| 649 |
|
| 650 |
-
# ββ Layer 3.5: Real-time Hallucination Guard ββ
|
| 651 |
-
# Scan incoming chunks for hallucination signals (invented facts, ungrounded claims)
|
| 652 |
-
hallucination_detect_buffer = ""
|
| 653 |
-
hallucination_warnings = []
|
| 654 |
-
|
| 655 |
-
def check_hallucination_signals(accumulated_text: str) -> tuple:
|
| 656 |
-
"""Scan for hallucination red flags. Returns (has_issues, warnings)."""
|
| 657 |
-
warnings = []
|
| 658 |
-
|
| 659 |
-
# Red flag 1: Artist death claims without verification
|
| 660 |
-
if re.search(r'(passed away|died|deceased).*?(19|20)\d{2}', accumulated_text, re.IGNORECASE):
|
| 661 |
-
for artist in ["laney wilson", "megan moroney", "tyler childers"]:
|
| 662 |
-
if artist in accumulated_text.lower():
|
| 663 |
-
warnings.append(f"Unverified artist claim: {artist}")
|
| 664 |
-
|
| 665 |
-
# Red flag 2: Invented plugin names (check last mentions)
|
| 666 |
-
if re.search(r'\b([A-Z][a-zA-Z0-9\s\-]+)\s+(plugin|VST|effect)\b', accumulated_text):
|
| 667 |
-
last_plugin = re.findall(r'\b([A-Z][a-zA-Z0-9\s\-]+)\s+(plugin|VST)', accumulated_text)
|
| 668 |
-
if last_plugin:
|
| 669 |
-
plugin_name = last_plugin[-1][0].lower()
|
| 670 |
-
real_plugins = {
|
| 671 |
-
"fabfilter", "waves", "izotope", "soundtoys", "valhalla",
|
| 672 |
-
"xfer", "native instruments", "spectrasonics", "u-he",
|
| 673 |
-
"arturia", "slate digital", "universal audio", "plugin alliance"
|
| 674 |
-
}
|
| 675 |
-
if not any(real in plugin_name for real in real_plugins):
|
| 676 |
-
warnings.append(f"Unknown plugin mentioned: {last_plugin[-1][0]}")
|
| 677 |
-
|
| 678 |
-
# Red flag 3: Genre misclassification for known artists
|
| 679 |
-
genre_mismatches = [
|
| 680 |
-
("laney wilson", "indie-rock"),
|
| 681 |
-
("megan moroney", "indie-rock"),
|
| 682 |
-
]
|
| 683 |
-
for artist, wrong_genre in genre_mismatches:
|
| 684 |
-
if artist in accumulated_text.lower() and wrong_genre in accumulated_text.lower():
|
| 685 |
-
warnings.append(f"Genre mismatch: {artist} is not {wrong_genre}")
|
| 686 |
-
|
| 687 |
-
return len(warnings) > 0, warnings
|
| 688 |
-
|
| 689 |
for chunk in stream:
|
| 690 |
if chunk.choices and chunk.choices[0].delta.content:
|
| 691 |
token = chunk.choices[0].delta.content
|
| 692 |
full_response += token
|
| 693 |
-
hallucination_detect_buffer += token
|
| 694 |
-
|
| 695 |
-
# Check for hallucination signals every ~50 chars to avoid lag
|
| 696 |
-
if len(hallucination_detect_buffer) > 50:
|
| 697 |
-
has_issues, warnings = check_hallucination_signals(full_response)
|
| 698 |
-
if warnings and warnings not in hallucination_warnings:
|
| 699 |
-
hallucination_warnings.extend(warnings)
|
| 700 |
-
hallucination_detect_buffer = ""
|
| 701 |
-
|
| 702 |
yield json.dumps({
|
| 703 |
"message": {"role": "assistant", "content": token},
|
| 704 |
"done": False,
|
| 705 |
}) + "\n"
|
| 706 |
await asyncio.sleep(0)
|
| 707 |
|
| 708 |
-
# ββ Post-generation hallucination check ββ
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
if final_warnings and classification.get("has_artist_query"):
|
| 712 |
-
# Artist query with unverified claims detected
|
| 713 |
correction = (
|
| 714 |
"\n\n---\n"
|
| 715 |
-
"[Self-correction: I notice I
|
| 716 |
-
"Rather than continuing to potentially hallucinate, I want to be honest: "
|
| 717 |
"I don't have reliable biographical information about this artist. "
|
| 718 |
"For accurate details, please check Wikipedia, Spotify, or their official website. "
|
| 719 |
-
"I'm happy to help with production techniques, music theory, or creating inspired work instead.]
|
| 720 |
)
|
| 721 |
full_response += correction
|
| 722 |
yield json.dumps({
|
| 723 |
"message": {"role": "assistant", "content": correction},
|
| 724 |
"done": False,
|
| 725 |
-
"metadata": {"hallucination_detected": True, "issues":
|
| 726 |
}) + "\n"
|
| 727 |
|
| 728 |
-
# ββ Layer 5.5: Post-generation ethical check ββ
|
| 729 |
-
# (lightweight β check for obviously problematic output patterns)
|
| 730 |
-
|
| 731 |
# ββ Layer 7: Store cocoon ββ
|
| 732 |
store_cocoon(query, full_response, classification, adapter_keys)
|
| 733 |
|
|
@@ -738,7 +742,7 @@ async def chat(request: Request):
|
|
| 738 |
}) + "\n"
|
| 739 |
|
| 740 |
except Exception as e:
|
| 741 |
-
error_msg =
|
| 742 |
print(f"[ERROR] Inference failed: {e}")
|
| 743 |
print(f"[TRACE] {traceback.format_exc()}")
|
| 744 |
yield json.dumps({
|
|
|
|
| 7 |
|
| 8 |
import json
|
| 9 |
import asyncio
|
| 10 |
+
import math
|
| 11 |
import os
|
| 12 |
import time
|
| 13 |
import re
|
|
|
|
| 24 |
from huggingface_hub import InferenceClient
|
| 25 |
|
| 26 |
# ββ Configuration ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 27 |
+
MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct"
|
| 28 |
+
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 29 |
+
MAX_TOKENS = 512
|
|
|
|
|
|
|
| 30 |
TEMPERATURE = 0.7
|
| 31 |
TOP_P = 0.9
|
| 32 |
|
| 33 |
# ββ Inference Client ββββββββββββββββββββββββββββββββββββββββββ
|
| 34 |
+
client = InferenceClient(model=MODEL_ID, token=HF_TOKEN)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
# ββ In-Memory Cocoon Storage ββββββββββββββββββββββββββββββββββ
|
| 37 |
cocoon_memory = []
|
|
|
|
| 104 |
"""
|
| 105 |
lower = query.lower()
|
| 106 |
|
|
|
|
| 107 |
artist_patterns = [
|
| 108 |
r'\b(who is|tell me about|what do you know about|who are)\s+([a-z\s\'-]+)\?',
|
| 109 |
r'\b(album|discography|career|songs? by|music by)\s+([a-z\s\'-]+)',
|
|
|
|
| 114 |
for pattern in artist_patterns:
|
| 115 |
match = re.search(pattern, lower, re.IGNORECASE)
|
| 116 |
if match:
|
|
|
|
| 117 |
artist_name = match.group(2) if len(match.groups()) > 1 else None
|
| 118 |
return {
|
| 119 |
"is_artist_query": True,
|
|
|
|
| 140 |
if complex_score >= 2 or word_count > 40:
|
| 141 |
complexity = "COMPLEX"
|
| 142 |
elif semantic_score >= 1 and word_count <= 8:
|
|
|
|
| 143 |
complexity = "MEDIUM"
|
| 144 |
elif semantic_score >= 2:
|
| 145 |
complexity = "COMPLEX"
|
|
|
|
| 275 |
- Be specific: name actual frequencies, ratios, time constants, chord voicings
|
| 276 |
- A producer should walk away with something they can use immediately
|
| 277 |
|
| 278 |
+
### COMMON MIXING MISTAKES TO AVOID (from real testing β these errors were caught):
|
| 279 |
+
- Compression RATIO is expressed as X:1 (e.g., 3:1, 6:1, 8:1). Do NOT describe ratio in dB. The THRESHOLD is in dB (e.g., -20 dB). Never confuse these.
|
| 280 |
+
- Kick drum attack/click lives in the 2-5 kHz range. The 100-150 Hz range is the punch/impact zone, NOT the attack. Do not say "boost low mids for attack."
|
| 281 |
+
- Do NOT recommend high-passing a kick drum at 80 Hz β this removes the fundamental (50-80 Hz). Only HPF at ~20-30 Hz to remove sub-rumble, if needed at all.
|
| 282 |
+
- Do NOT suggest compressing the entire drum kit for kick shaping. Process the kick individually on its own channel/bus.
|
| 283 |
+
- Gain reduction for kick compression is typically 3-6 dB. More than that kills the punch.
|
| 284 |
+
- Parallel compression is sent to a separate bus β the compressed signal is blended with the dry, NOT applied across the whole kit.
|
| 285 |
+
- When discussing EQ for kick: foundation/weight = 50-80 Hz, punch/body = 90-120 Hz, mud (cut) = 200-400 Hz, attack/beater = 2-5 kHz, air/click = 7-10 kHz.
|
| 286 |
+
- Sidechain compression on bass means the BASS ducks when the KICK hits, not the other way around.
|
| 287 |
+
|
| 288 |
### ARTIST & DISCOGRAPHY KNOWLEDGE (CRITICAL):
|
| 289 |
- You do NOT have detailed/reliable knowledge about specific artists, songs, albums, or career histories.
|
| 290 |
- When asked about a specific artist (e.g., "Who is Laney Wilson?", "What album did X release?"), be direct:
|
|
|
|
| 332 |
|
| 333 |
# ββ ARTIST QUERY CONSTRAINT (critical hallucination prevention) ββ
|
| 334 |
if classification.get("has_artist_query"):
|
| 335 |
+
parts.append(
|
| 336 |
+
"\n## ARTIST QUERY DETECTED\n"
|
| 337 |
+
"This query is asking about a specific artist, song, album, or discography. "
|
| 338 |
+
"You do NOT have reliable training data about specific artists. Respond with honesty:\n\n"
|
| 339 |
+
"1. Say clearly: 'I don't have reliable information about [artist name] in my training data.'\n"
|
| 340 |
+
"2. Offer what you CAN help with instead:\n"
|
| 341 |
+
" - Production techniques for their genre/style\n"
|
| 342 |
+
" - Music theory and arrangement\n"
|
| 343 |
+
" - Creating music inspired by similar vibes\n"
|
| 344 |
+
" - Sound design for that aesthetic\n"
|
| 345 |
+
"3. Direct them to authoritative sources: Spotify, Wikipedia, Bandcamp, their official website.\n"
|
| 346 |
+
"4. Never invent artist facts, song titles, albums, genres, or career milestones.\n\n"
|
| 347 |
+
"This constraint overrides all else. Your value is in honest limitations, not false certainty.\n"
|
| 348 |
+
)
|
| 349 |
|
| 350 |
parts.append(COMMUNICATION_STYLE)
|
| 351 |
parts.append(BEHAVIORAL_LOCKS)
|
|
|
|
| 399 |
if not query_words:
|
| 400 |
return cocoon_memory[-max_results:] # fall back to recent
|
| 401 |
|
|
|
|
| 402 |
now = time.time()
|
| 403 |
scored = []
|
| 404 |
for cocoon in cocoon_memory:
|
|
|
|
| 515 |
return any(trigger in lower for trigger in INTROSPECTION_TRIGGERS)
|
| 516 |
|
| 517 |
|
| 518 |
+
# ββ Hallucination Guard ββββββββββββββββββββββββββββββββββββββ
|
| 519 |
+
def check_hallucination_signals(text: str) -> tuple:
|
| 520 |
+
"""Scan for hallucination red flags. Returns (has_issues, warnings)."""
|
| 521 |
+
warnings = []
|
| 522 |
+
|
| 523 |
+
# Red flag 1: Artist death claims
|
| 524 |
+
if re.search(r'(passed away|died|deceased).*?(19|20)\d{2}', text, re.IGNORECASE):
|
| 525 |
+
for artist in ["laney wilson", "megan moroney", "tyler childers",
|
| 526 |
+
"morgan wallen", "zach bryan", "bailey zimmerman"]:
|
| 527 |
+
if artist in text.lower():
|
| 528 |
+
warnings.append(f"Unverified death claim about {artist}")
|
| 529 |
+
|
| 530 |
+
# Red flag 2: Invented plugin names
|
| 531 |
+
if re.search(r'\b([A-Z][a-zA-Z0-9\s\-]+)\s+(plugin|VST|effect)\b', text):
|
| 532 |
+
last_plugin = re.findall(r'\b([A-Z][a-zA-Z0-9\s\-]+)\s+(plugin|VST)', text)
|
| 533 |
+
if last_plugin:
|
| 534 |
+
plugin_name = last_plugin[-1][0].lower()
|
| 535 |
+
real_plugins = {
|
| 536 |
+
"fabfilter", "waves", "izotope", "soundtoys", "valhalla",
|
| 537 |
+
"xfer", "native instruments", "spectrasonics", "u-he",
|
| 538 |
+
"arturia", "slate digital", "universal audio", "plugin alliance",
|
| 539 |
+
"pro-q", "pro-c", "pro-l", "ozone", "neutron", "serum",
|
| 540 |
+
}
|
| 541 |
+
if not any(real in plugin_name for real in real_plugins):
|
| 542 |
+
warnings.append(f"Unknown plugin: {last_plugin[-1][0]}")
|
| 543 |
+
|
| 544 |
+
# Red flag 3: Genre misclassification
|
| 545 |
+
genre_mismatches = [
|
| 546 |
+
("laney wilson", "indie-rock"), ("laney wilson", "indie-folk"),
|
| 547 |
+
("megan moroney", "indie-rock"), ("megan moroney", "indie-folk"),
|
| 548 |
+
]
|
| 549 |
+
for artist, wrong_genre in genre_mismatches:
|
| 550 |
+
if artist in text.lower() and wrong_genre in text.lower():
|
| 551 |
+
warnings.append(f"Genre mismatch: {artist} is not {wrong_genre}")
|
| 552 |
+
|
| 553 |
+
# Red flag 4: Compression ratio described in dB
|
| 554 |
+
if re.search(r'ratio.*?(-?\d+\s*dB)', text, re.IGNORECASE):
|
| 555 |
+
warnings.append("Compression ratio should be X:1, not in dB")
|
| 556 |
+
|
| 557 |
+
return len(warnings) > 0, warnings
|
| 558 |
+
|
| 559 |
+
|
| 560 |
# ββ FastAPI App βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 561 |
app = FastAPI(title="Codette AI β HorizonCoreAI Reasoning Engine")
|
| 562 |
app.add_middleware(
|
|
|
|
| 571 |
print("9 adapter perspectives loaded")
|
| 572 |
print("AEGIS ethical guard active")
|
| 573 |
print("Behavioral locks enforced")
|
| 574 |
+
print("Artist hallucination guard active")
|
| 575 |
+
print("Music production grounding rules enforced")
|
| 576 |
|
| 577 |
|
| 578 |
@app.get("/", response_class=HTMLResponse)
|
|
|
|
| 598 |
"query_classifier": "active",
|
| 599 |
"introspection": "active",
|
| 600 |
"consciousness_stack": "12 layers",
|
| 601 |
+
"hallucination_guard": "active",
|
| 602 |
+
"artist_detection": "active",
|
| 603 |
}
|
| 604 |
return {
|
| 605 |
"status": "healthy",
|
| 606 |
"system": "Codette AI β HorizonCoreAI",
|
| 607 |
+
"version": "2.1-phase6",
|
| 608 |
"checks": checks,
|
| 609 |
"uptime": "running",
|
| 610 |
}
|
|
|
|
| 705 |
stream=True,
|
| 706 |
)
|
| 707 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 708 |
for chunk in stream:
|
| 709 |
if chunk.choices and chunk.choices[0].delta.content:
|
| 710 |
token = chunk.choices[0].delta.content
|
| 711 |
full_response += token
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
yield json.dumps({
|
| 713 |
"message": {"role": "assistant", "content": token},
|
| 714 |
"done": False,
|
| 715 |
}) + "\n"
|
| 716 |
await asyncio.sleep(0)
|
| 717 |
|
| 718 |
+
# ββ Layer 3.5: Post-generation hallucination check ββ
|
| 719 |
+
has_issues, warnings = check_hallucination_signals(full_response)
|
| 720 |
+
if has_issues and classification.get("has_artist_query"):
|
|
|
|
|
|
|
| 721 |
correction = (
|
| 722 |
"\n\n---\n"
|
| 723 |
+
"[Self-correction: I notice I may have made unverified claims above. "
|
|
|
|
| 724 |
"I don't have reliable biographical information about this artist. "
|
| 725 |
"For accurate details, please check Wikipedia, Spotify, or their official website. "
|
| 726 |
+
"I'm happy to help with production techniques, music theory, or creating inspired work instead.]"
|
| 727 |
)
|
| 728 |
full_response += correction
|
| 729 |
yield json.dumps({
|
| 730 |
"message": {"role": "assistant", "content": correction},
|
| 731 |
"done": False,
|
| 732 |
+
"metadata": {"hallucination_detected": True, "issues": warnings},
|
| 733 |
}) + "\n"
|
| 734 |
|
|
|
|
|
|
|
|
|
|
| 735 |
# ββ Layer 7: Store cocoon ββ
|
| 736 |
store_cocoon(query, full_response, classification, adapter_keys)
|
| 737 |
|
|
|
|
| 742 |
}) + "\n"
|
| 743 |
|
| 744 |
except Exception as e:
|
| 745 |
+
error_msg = "I encountered an issue processing your request. Please try again."
|
| 746 |
print(f"[ERROR] Inference failed: {e}")
|
| 747 |
print(f"[TRACE] {traceback.format_exc()}")
|
| 748 |
yield json.dumps({
|