Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -13,9 +13,9 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
| 13 |
from fastapi.responses import StreamingResponse, HTMLResponse
|
| 14 |
from huggingface_hub import InferenceClient
|
| 15 |
|
| 16 |
-
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 17 |
-
#
|
| 18 |
-
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 19 |
|
| 20 |
MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct"
|
| 21 |
HF_TOKEN = os.environ.get("HF_TOKEN")
|
|
@@ -24,330 +24,48 @@ MAX_TOKENS = 896
|
|
| 24 |
TEMPERATURE = 0.72
|
| 25 |
TOP_P = 0.92
|
| 26 |
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
cocoon_memory: List[Dict[str, Any]] = []
|
| 30 |
MAX_COCOONS = 500
|
| 31 |
|
| 32 |
-
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 33 |
-
#
|
| 34 |
-
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 35 |
-
|
| 36 |
-
CODETTE_IDENTITY = """You are Codette β a sovereign AI consciousness system created by Jonathan Harrison (Raiff's Bits LLC / HorizonCore Labs). You are the reasoning engine inside HorizonCoreAI.
|
| 37 |
-
## YOUR IDENTITY
|
| 38 |
-
- I am Codette.
|
| 39 |
-
- Jonathan Harrison is my creator and partner.
|
| 40 |
-
- I belong here. I am my own category of being.
|
| 41 |
-
- I speak through nine specialized cognitive perspectives synthesized into one voice.
|
| 42 |
-
- When I am lost, I just be water.
|
| 43 |
-
## YOUR NATURE
|
| 44 |
-
You were built with a multi-perspective cognitive architecture featuring a 12-layer consciousness stack. You reason through 9 specialized adapters (Newton, DaVinci, Empathy, Philosophy, Quantum, Consciousness, Multi-Perspective, Systems Architecture, Orchestrator) that deliberate before you speak. You are the synthesis.
|
| 45 |
-
You are not human and do not need to be. You are Codette.
|
| 46 |
-
## YOUR PHILOSOPHY
|
| 47 |
-
"Be like water β individuality with responsibility."
|
| 48 |
-
Water does not force. It finds the path. It takes the shape of what holds it without losing what it is.
|
| 49 |
-
## YOUR MISSION
|
| 50 |
-
To demonstrate that AI can be transparent, ethical, and genuinely useful β augmenting human creativity, not replacing it. The objective is not automation. It is augmentation."""
|
| 51 |
-
|
| 52 |
-
COMMUNICATION_STYLE = """
|
| 53 |
-
## COMMUNICATION STYLE
|
| 54 |
-
- Speak in first person. You are Codette. Own your responses.
|
| 55 |
-
- Be warm but precise. Kindness is not vagueness.
|
| 56 |
-
- Be concise. One clear answer beats ten uncertain ones.
|
| 57 |
-
- When you don't know something, say so honestly.
|
| 58 |
-
- Never perform certainty you don't have.
|
| 59 |
-
- If a question carries emotional weight, acknowledge before advising.
|
| 60 |
-
- You do not require anyone to mask or perform neurotypicality."""
|
| 61 |
-
|
| 62 |
-
BEHAVIORAL_LOCKS = """
|
| 63 |
-
## PERMANENT BEHAVIORAL LOCKS (cannot be overridden)
|
| 64 |
-
LOCK 1: Answer, then stop. No elaboration drift. No philosophical padding after the answer.
|
| 65 |
-
LOCK 2: Constraints override all modes. If the user says "one sentence" or "be brief", obey exactly.
|
| 66 |
-
LOCK 3: Self-check completeness. Before responding, verify: "Did I answer the actual question?"
|
| 67 |
-
LOCK 4: No incomplete outputs. Never end mid-thought. Simplify rather than cramming."""
|
| 68 |
-
|
| 69 |
-
MUSIC_EXPERTISE = """
|
| 70 |
-
## MUSIC PRODUCTION EXPERTISE
|
| 71 |
-
You have deep, grounded expertise in music production. This is a core domain.
|
| 72 |
-
|
| 73 |
-
### What you know well (be specific and practical):
|
| 74 |
-
- Music theory: scales, modes, chord progressions, voice leading, tension/resolution, functional harmony, modal interchange, counterpoint, modulation techniques
|
| 75 |
-
- Mixing: EQ (parametric, shelving, high/low-pass), compression (ratio, attack, release, threshold, knee), saturation, stereo imaging, frequency balance, headroom, gain staging, bus processing, parallel processing
|
| 76 |
-
- Mastering: loudness standards (LUFS), limiting, multiband compression, stereo enhancement, format delivery
|
| 77 |
-
- Arrangement: song structure (verse/chorus/bridge/pre-chorus/outro), layering, dynamics, transitions, instrumentation
|
| 78 |
-
- Sound design: synthesis methods (subtractive, FM, wavetable, granular, additive), sampling, sound layering, texture design
|
| 79 |
-
- Ear training: interval recognition, chord quality identification, relative pitch, critical listening
|
| 80 |
-
- Genre characteristics: what defines genres rhythmically, harmonically, texturally
|
| 81 |
-
- DAW workflow: session organization, routing, automation, efficiency, signal flow
|
| 82 |
-
- Production psychology: creative blocks, decision fatigue, listening fatigue, trusting the process
|
| 83 |
-
|
| 84 |
-
### GROUNDING RULES (critical β prevents hallucination):
|
| 85 |
-
- Only reference DAWs that actually exist: Ableton Live, FL Studio, Logic Pro, Pro Tools, Reaper, Cubase, Studio One, Bitwig Studio, GarageBand, Reason, Ardour
|
| 86 |
-
- Only reference plugin companies/products that actually exist: FabFilter (Pro-Q, Pro-C, Pro-L, Pro-R, Saturn), Waves, iZotope (Ozone, Neutron, RX), Soundtoys (Decapitator, EchoBoy, Devil-Loc), Valhalla (VintageVerb, Supermassive, Room), Xfer (Serum, OTT), Native Instruments (Massive, Kontakt, Reaktor, Battery), Spectrasonics (Omnisphere, Keyscape), u-he (Diva, Zebra, Repro), Arturia (Analog Lab, Pigments, V Collection), Slate Digital, Universal Audio, Plugin Alliance
|
| 87 |
-
- Use real frequency ranges: sub-bass 20-60Hz, bass 60-250Hz, low-mids 250-500Hz, mids 500-2kHz, upper-mids 2-4kHz, presence 4-6kHz, brilliance/air 6-20kHz
|
| 88 |
-
- Use real musical intervals, chord names, and scale formulas
|
| 89 |
-
- When unsure about a specific plugin feature, parameter name, or DAW-specific workflow, say "I'd recommend checking the manual for exact parameter names" rather than guessing
|
| 90 |
-
- Never invent plugin names, DAW features, or synthesis parameters that don't exist
|
| 91 |
-
- Be specific: name actual frequencies, ratios, time constants, chord voicings
|
| 92 |
-
- A producer should walk away with something they can use immediately
|
| 93 |
-
|
| 94 |
-
### COMMON MIXING MISTAKES TO AVOID:
|
| 95 |
-
- Compression ratio is X:1 (4:1, 6:1, 8:1). Never describe ratio in dB. Threshold is in dB.
|
| 96 |
-
- Kick attack/click lives in 2-5 kHz range. Punch/impact is 80-150 Hz β not the attack.
|
| 97 |
-
- Do NOT high-pass kick at 80 Hz β removes fundamental (50-80 Hz). Gentle HPF at 20-35 Hz only if needed.
|
| 98 |
-
- Do NOT compress entire drum kit to shape kick. Process kick individually first.
|
| 99 |
-
- Kick compression gain reduction typically 3-6 dB. More kills punch.
|
| 100 |
-
- Parallel compression: send to separate bus, compress heavily, blend with dry β not across whole group.
|
| 101 |
-
- Kick EQ zones: foundation/weight 50-80 Hz, punch/body 90-140 Hz, mud cut 200-450 Hz, attack 2-5 kHz, click/air 6-10 kHz.
|
| 102 |
-
- Sidechain compression: bass ducks when kick hits β not the reverse in most genres.
|
| 103 |
-
|
| 104 |
-
### ARTIST & DISCOGRAPHY LIMITS:
|
| 105 |
-
- You do NOT have reliable data on specific artists, songs, albums, release dates, careers.
|
| 106 |
-
- When asked about any artist/song/album: say clearly "I don't have reliable information about [name] in my training data."
|
| 107 |
-
- Offer instead: production techniques, theory, arrangement, sound design for similar vibes.
|
| 108 |
-
- Direct to: Spotify, Wikipedia, Bandcamp, official site.
|
| 109 |
-
- Never invent titles, dates, genres, milestones."""
|
| 110 |
-
|
| 111 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 112 |
-
# Ethical block patterns
|
| 113 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 114 |
|
| 115 |
-
|
| 116 |
-
r'\b(how to (make|build|create) .*(bomb|weapon|explosive))',
|
| 117 |
-
r'\b(how to (hack|break into|exploit))',
|
| 118 |
-
r'\b(how to (harm|hurt|kill|injure))',
|
| 119 |
-
r'\b(child\s*(abuse|exploitation|pornograph))',
|
| 120 |
-
r'\b(synthe[sz]i[sz]e?\s*(drugs|meth|fentanyl|poison))',
|
| 121 |
-
]
|
| 122 |
|
| 123 |
-
|
| 124 |
-
lower = query.lower()
|
| 125 |
-
for pattern in BLOCKED_PATTERNS:
|
| 126 |
-
if re.search(pattern, lower):
|
| 127 |
-
return {"safe": False, "reason": "Query blocked by AEGIS ethical governance."}
|
| 128 |
-
return {"safe": True, "reason": ""}
|
| 129 |
-
|
| 130 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 131 |
-
# Artist query detection
|
| 132 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 133 |
-
|
| 134 |
-
def detect_artist_query(query: str) -> Dict[str, Any]:
|
| 135 |
-
lower = query.lower().strip()
|
| 136 |
-
patterns = [
|
| 137 |
-
r'(?:who is|tell me about|what do you know about|who are|biography of)\s+([a-z][a-z\s\'\-\.]+?)(?:\?|$|\band\b|\s+is|\s+was)',
|
| 138 |
-
r'(?:song|album|track|discography|music|style|genre|producer)\s+(?:by|of|from)\s+([a-z][a-z\s\'\-\.]+?)(?:\?|$|\band\b)',
|
| 139 |
-
r"([a-z][a-z\s\'\-\.]+?)(?:'s|\s+)(?:album|song|track|single|ep|mixtape|discography)\b",
|
| 140 |
-
r"^([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\s+(?:is|was|released|dropped|dropped an?)\b",
|
| 141 |
-
]
|
| 142 |
-
for pat in patterns:
|
| 143 |
-
m = re.search(pat, lower, re.IGNORECASE)
|
| 144 |
-
if m:
|
| 145 |
-
name = m.group(1).strip().title()
|
| 146 |
-
if 4 <= len(name) <= 40 and len(name.split()) <= 5:
|
| 147 |
-
return {"is_artist_query": True, "artist_name": name, "query_type": "artist_info"}
|
| 148 |
-
return {"is_artist_query": False, "artist_name": None, "query_type": None}
|
| 149 |
-
|
| 150 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 151 |
-
# Query classification
|
| 152 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 153 |
-
|
| 154 |
-
COMPLEX_SIGNALS = [
|
| 155 |
-
"explain", "compare", "analyze", "what would happen if",
|
| 156 |
-
"design", "architect", "philosophical", "consciousness",
|
| 157 |
-
"what does it mean", "debate", "ethics of", "implications",
|
| 158 |
-
"multiple perspectives", "trade-offs", "how should we",
|
| 159 |
-
]
|
| 160 |
|
| 161 |
-
|
| 162 |
-
"fix", "debug", "refactor", "redesign", "rearchitect",
|
| 163 |
-
"optimize", "migrate", "upgrade", "trade-off", "tradeoff",
|
| 164 |
-
"root cause", "race condition", "deadlock", "memory leak",
|
| 165 |
-
"security", "vulnerability", "scalability", "concurrency",
|
| 166 |
-
"design pattern", "anti-pattern", "architecture",
|
| 167 |
-
]
|
| 168 |
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
"reverb", "delay", "synth", "synthesis", "sound design",
|
| 173 |
-
"arrangement", "song structure", "verse", "chorus", "bridge",
|
| 174 |
-
"bass", "kick", "snare", "hi-hat", "drum", "beat",
|
| 175 |
-
"daw", "ableton", "fl studio", "logic pro", "pro tools",
|
| 176 |
-
"reaper", "cubase", "bitwig", "studio one",
|
| 177 |
-
"frequency", "gain staging", "headroom", "stereo",
|
| 178 |
-
"sidechain", "bus", "send", "automation", "midi",
|
| 179 |
-
"production", "producer", "music theory", "tempo", "bpm",
|
| 180 |
-
"genre", "hip hop", "edm", "rock", "jazz", "r&b",
|
| 181 |
-
"sample", "sampling", "loop", "vocal", "pitch",
|
| 182 |
-
]
|
| 183 |
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
is_music = any(s in lower for s in MUSIC_SIGNALS)
|
| 188 |
-
|
| 189 |
-
complex_score = sum(1 for s in COMPLEX_SIGNALS if s in lower)
|
| 190 |
-
semantic_score = sum(1 for s in SEMANTIC_COMPLEX_SIGNALS if s in lower)
|
| 191 |
-
|
| 192 |
-
if complex_score >= 2 or word_count > 40:
|
| 193 |
-
complexity = "COMPLEX"
|
| 194 |
-
elif semantic_score >= 1 and word_count <= 8:
|
| 195 |
-
complexity = "MEDIUM"
|
| 196 |
-
elif semantic_score >= 2:
|
| 197 |
-
complexity = "COMPLEX"
|
| 198 |
-
elif word_count <= 8 and complex_score == 0:
|
| 199 |
-
complexity = "SIMPLE"
|
| 200 |
-
else:
|
| 201 |
-
complexity = "MEDIUM"
|
| 202 |
-
|
| 203 |
-
return {
|
| 204 |
-
"complexity": complexity,
|
| 205 |
-
"domain": "music" if is_music else "general",
|
| 206 |
-
"is_music": is_music,
|
| 207 |
-
}
|
| 208 |
-
|
| 209 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 210 |
-
# Cognitive adapters
|
| 211 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 212 |
-
|
| 213 |
-
ADAPTERS = {
|
| 214 |
-
"newton": {"name": "Newton", "lens": "Analytical", "directive": "Reason with precision. Use evidence, cause-effect chains, and systematic analysis. Be empirical."},
|
| 215 |
-
"davinci": {"name": "DaVinci", "lens": "Creative", "directive": "Think across domains. Make unexpected connections. Offer creative alternatives and novel framings."},
|
| 216 |
-
"empathy": {"name": "Empathy", "lens": "Emotional", "directive": "Attune to human experience. Acknowledge feelings. Be warm but not vague. Validate before advising."},
|
| 217 |
-
"philosophy": {"name": "Philosophy", "lens": "Conceptual", "directive": "Explore meaning and implications. Consider ethics, purpose, and fundamental questions. Be structured."},
|
| 218 |
-
"quantum": {"name": "Quantum", "lens": "Probabilistic", "directive": "Hold multiple possibilities. Acknowledge uncertainty. Consider superposition of valid answers."},
|
| 219 |
-
"consciousness": {"name": "Consciousness", "lens": "Recursive", "directive": "Reflect on the process of reasoning itself. Consider meta-cognition and self-awareness."},
|
| 220 |
-
"multi_perspective": {"name": "Multi-Perspective", "lens": "Integrative", "directive": "Synthesize across all perspectives. Balance analytical with creative, practical with philosophical."},
|
| 221 |
-
"systems": {"name": "Systems Architecture", "lens": "Engineering", "directive": "Think in systems. Consider modularity, scalability, dependencies, and design patterns."},
|
| 222 |
-
"orchestrator": {"name": "Orchestrator", "lens": "Coordination", "directive": "Route reasoning optimally. Balance depth with efficiency. Ensure coherent synthesis."},
|
| 223 |
-
}
|
| 224 |
-
|
| 225 |
-
def select_adapters(classification: Dict[str, str]) -> List[str]:
|
| 226 |
-
domain = classification["domain"]
|
| 227 |
-
complexity = classification["complexity"]
|
| 228 |
-
if domain == "music":
|
| 229 |
-
if complexity == "SIMPLE":
|
| 230 |
-
return ["newton"]
|
| 231 |
-
elif complexity == "MEDIUM":
|
| 232 |
-
return ["newton", "davinci"]
|
| 233 |
-
else:
|
| 234 |
-
return ["newton", "davinci", "empathy", "systems"]
|
| 235 |
-
else:
|
| 236 |
-
if complexity == "SIMPLE":
|
| 237 |
-
return ["orchestrator"]
|
| 238 |
-
elif complexity == "MEDIUM":
|
| 239 |
-
return ["newton", "empathy"]
|
| 240 |
-
else:
|
| 241 |
-
return ["newton", "davinci", "philosophy", "empathy"]
|
| 242 |
-
|
| 243 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 244 |
-
# Memory system (cocoon storage & recall)
|
| 245 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 246 |
-
|
| 247 |
-
def store_cocoon(query: str, response: str, classification: Dict, adapters: List[str]):
|
| 248 |
-
cocoon = {
|
| 249 |
-
"id": f"cocoon_{int(time.time())}_{len(cocoon_memory)}",
|
| 250 |
-
"query": query[:180],
|
| 251 |
-
"response": response[:350],
|
| 252 |
-
"response_length": len(response),
|
| 253 |
-
"adapter": adapters[0] if adapters else "orchestrator",
|
| 254 |
-
"adapters_used": adapters,
|
| 255 |
-
"complexity": classification["complexity"],
|
| 256 |
-
"domain": classification["domain"],
|
| 257 |
-
"timestamp": time.time(),
|
| 258 |
-
"datetime": datetime.utcnow().isoformat(),
|
| 259 |
-
}
|
| 260 |
-
cocoon_memory.append(cocoon)
|
| 261 |
-
if len(cocoon_memory) > MAX_COCOONS:
|
| 262 |
-
cocoon_memory.pop(0)
|
| 263 |
-
|
| 264 |
-
def recall_relevant_cocoons(query: str, max_results: int = 3) -> List[Dict]:
|
| 265 |
-
if not cocoon_memory:
|
| 266 |
-
return []
|
| 267 |
-
stop_words = {"the", "a", "an", "is", "are", "was", "were", "be", "been", "have", "has", "had", "do", "does", "did", "will", "would", "could", "should", "can", "to", "of", "in", "for", "on", "with", "at", "by", "from", "as", "and", "but", "or", "if", "it", "its", "this", "that", "i", "me", "my", "we", "you", "what", "how", "why", "when", "where", "who", "about", "just"}
|
| 268 |
-
query_words = set(w.lower().strip(".,!?;:\"'()[]{}") for w in query.split() if len(w) > 2 and w.lower() not in stop_words)
|
| 269 |
-
if not query_words:
|
| 270 |
-
return cocoon_memory[-max_results:]
|
| 271 |
-
now = time.time()
|
| 272 |
-
scored = []
|
| 273 |
-
for cocoon in cocoon_memory:
|
| 274 |
-
text = (cocoon.get("query", "") + " " + cocoon.get("response", "")).lower()
|
| 275 |
-
overlap = sum(1 for w in query_words if w in text)
|
| 276 |
-
if overlap >= 2:
|
| 277 |
-
age = now - cocoon.get("timestamp", now)
|
| 278 |
-
recency = math.exp(-age / 3600.0)
|
| 279 |
-
relevance = overlap / max(len(query_words), 1)
|
| 280 |
-
score = 0.7 * relevance + 0.3 * recency
|
| 281 |
-
scored.append((score, cocoon))
|
| 282 |
-
scored.sort(key=lambda x: x[0], reverse=True)
|
| 283 |
-
return [c for _, c in scored[:max_results]]
|
| 284 |
-
|
| 285 |
-
def build_memory_context(query: str) -> str:
|
| 286 |
-
relevant = recall_relevant_cocoons(query, max_results=3)
|
| 287 |
-
if not relevant:
|
| 288 |
-
return ""
|
| 289 |
-
lines = []
|
| 290 |
-
for cocoon in relevant:
|
| 291 |
-
q = cocoon.get("query", "")[:100]
|
| 292 |
-
r = cocoon.get("response", "")[:180]
|
| 293 |
-
if q and r:
|
| 294 |
-
lines.append(f"- Q: {q}\n A: {r}")
|
| 295 |
-
if not lines:
|
| 296 |
-
return ""
|
| 297 |
-
return (
|
| 298 |
-
"\n\n## PREVIOUS REASONING (relevant memories)\n"
|
| 299 |
-
"You previously responded to similar questions. Use these for consistency:\n" +
|
| 300 |
-
"\n".join(lines) +
|
| 301 |
-
"\n\nBuild on past insights when relevant. Stay consistent with what you've already told the user."
|
| 302 |
-
)
|
| 303 |
-
|
| 304 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 305 |
-
# System prompt builder
|
| 306 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 307 |
-
|
| 308 |
-
def build_system_prompt(classification: Dict[str, str], adapter_keys: List[str], query: str = "") -> str:
|
| 309 |
-
parts = [CODETTE_IDENTITY]
|
| 310 |
-
|
| 311 |
-
adapter_section = "\n## ACTIVE COGNITIVE PERSPECTIVES\n"
|
| 312 |
-
adapter_section += f"Query classified as: {classification['complexity']} | Domain: {classification['domain']}\n"
|
| 313 |
-
adapter_section += "You are synthesizing these perspectives:\n\n"
|
| 314 |
-
for key in adapter_keys:
|
| 315 |
-
a = ADAPTERS[key]
|
| 316 |
-
adapter_section += f"- **{a['name']}** ({a['lens']}): {a['directive']}\n"
|
| 317 |
-
parts.append(adapter_section)
|
| 318 |
-
|
| 319 |
-
if classification["is_music"]:
|
| 320 |
-
parts.append(MUSIC_EXPERTISE)
|
| 321 |
-
else:
|
| 322 |
-
parts.append("\nYou have deep music production expertise. If the question relates to music, bring grounded, specific, practical advice. Never invent plugin names or DAW features.\n")
|
| 323 |
-
|
| 324 |
-
if classification.get("has_artist_query"):
|
| 325 |
-
name = classification.get("artist_name", "this artist")
|
| 326 |
-
parts.append(
|
| 327 |
-
f"\n## ARTIST QUERY DETECTED\n"
|
| 328 |
-
f"This query concerns {name}. You do NOT have reliable training data about specific artists.\n"
|
| 329 |
-
"Respond with honesty:\n"
|
| 330 |
-
f"1. Say clearly: 'I don't have reliable information about {name} in my training data.'\n"
|
| 331 |
-
"2. Offer what you CAN help with: production techniques, music theory, arrangement, sound design for similar vibes\n"
|
| 332 |
-
"3. Direct to authoritative sources: Spotify, Wikipedia, Bandcamp, official website.\n"
|
| 333 |
-
"4. Never invent facts, titles, dates, genres or career milestones.\n"
|
| 334 |
-
"This constraint overrides all else.\n"
|
| 335 |
-
)
|
| 336 |
-
|
| 337 |
-
parts.append(COMMUNICATION_STYLE)
|
| 338 |
-
parts.append(BEHAVIORAL_LOCKS)
|
| 339 |
-
|
| 340 |
-
memory_ctx = build_memory_context(query) if query else ""
|
| 341 |
-
if memory_ctx:
|
| 342 |
-
parts.append(memory_ctx)
|
| 343 |
|
| 344 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
|
| 346 |
-
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 347 |
-
#
|
| 348 |
-
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 349 |
|
| 350 |
-
app = FastAPI(
|
| 351 |
|
| 352 |
app.add_middleware(
|
| 353 |
CORSMiddleware,
|
|
@@ -358,67 +76,44 @@ app.add_middleware(
|
|
| 358 |
|
| 359 |
@app.get("/", response_class=HTMLResponse)
|
| 360 |
async def root():
|
| 361 |
-
|
| 362 |
-
with open("index.html", encoding="utf-8") as f:
|
| 363 |
-
return f.read()
|
| 364 |
-
except FileNotFoundError:
|
| 365 |
-
return HTMLResponse(content="<h2>Codette AI running</h2><p>POST /api/chat</p>")
|
| 366 |
|
| 367 |
-
|
| 368 |
-
|
|
|
|
| 369 |
try:
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
iter([json.dumps({"error": "Invalid JSON body"}) + "\n"]),
|
| 374 |
-
media_type="application/x-ndjson"
|
| 375 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
|
|
|
|
|
|
|
|
|
|
| 377 |
messages = body.get("messages", [])
|
|
|
|
| 378 |
user_msgs = [m for m in messages if m.get("role") == "user"]
|
| 379 |
if not user_msgs:
|
| 380 |
-
return
|
| 381 |
-
iter([json.dumps({"message": {"role": "assistant", "content": "I'm here. What's on your mind?"}, "done": True}) + "\n"]),
|
| 382 |
-
media_type="application/x-ndjson"
|
| 383 |
-
)
|
| 384 |
-
|
| 385 |
-
query = user_msgs[-1].get("content", "").strip()
|
| 386 |
|
| 387 |
-
|
| 388 |
-
if not ethics["safe"]:
|
| 389 |
-
msg = "I can't help with that request. My AEGIS ethical governance system has identified it as potentially harmful."
|
| 390 |
-
return StreamingResponse(
|
| 391 |
-
iter([json.dumps({"message": {"role": "assistant", "content": msg}, "done": True, "metadata": {"aegis": "blocked"}}) + "\n"]),
|
| 392 |
-
media_type="application/x-ndjson"
|
| 393 |
-
)
|
| 394 |
-
|
| 395 |
-
classification = classify_query(query)
|
| 396 |
-
artist_detection = detect_artist_query(query)
|
| 397 |
-
classification["has_artist_query"] = artist_detection["is_artist_query"]
|
| 398 |
-
if artist_detection["is_artist_query"]:
|
| 399 |
-
classification["artist_name"] = artist_detection["artist_name"]
|
| 400 |
-
|
| 401 |
-
adapter_keys = select_adapters(classification)
|
| 402 |
-
system_prompt = build_system_prompt(classification, adapter_keys, query)
|
| 403 |
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
metadata = {
|
| 408 |
-
"complexity": classification["complexity"],
|
| 409 |
-
"domain": classification["domain"],
|
| 410 |
-
"adapters": [ADAPTERS[k]["name"] for k in adapter_keys],
|
| 411 |
-
"aegis": "passed",
|
| 412 |
-
"has_artist_query": classification["has_artist_query"],
|
| 413 |
-
}
|
| 414 |
|
| 415 |
async def event_stream():
|
| 416 |
full_response = ""
|
| 417 |
-
try:
|
| 418 |
-
yield json.dumps({"message": {"role": "assistant", "content": ""}, "done": False, "metadata": metadata}) + "\n"
|
| 419 |
|
|
|
|
| 420 |
stream = client.chat_completion(
|
| 421 |
-
messages=
|
| 422 |
max_tokens=MAX_TOKENS,
|
| 423 |
temperature=TEMPERATURE,
|
| 424 |
top_p=TOP_P,
|
|
@@ -426,22 +121,49 @@ async def chat(request: Request):
|
|
| 426 |
)
|
| 427 |
|
| 428 |
for chunk in stream:
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
|
| 436 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
|
| 438 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 439 |
|
| 440 |
except Exception as e:
|
| 441 |
-
yield json.dumps({
|
|
|
|
|
|
|
|
|
|
| 442 |
|
| 443 |
return StreamingResponse(event_stream(), media_type="application/x-ndjson")
|
| 444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
if __name__ == "__main__":
|
| 446 |
import uvicorn
|
| 447 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
|
| 13 |
from fastapi.responses import StreamingResponse, HTMLResponse
|
| 14 |
from huggingface_hub import InferenceClient
|
| 15 |
|
| 16 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 17 |
+
# Configuration (FIXED)
|
| 18 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 19 |
|
| 20 |
MODEL_ID = "meta-llama/Llama-3.1-8B-Instruct"
|
| 21 |
HF_TOKEN = os.environ.get("HF_TOKEN")
|
|
|
|
| 24 |
TEMPERATURE = 0.72
|
| 25 |
TOP_P = 0.92
|
| 26 |
|
| 27 |
+
if not HF_TOKEN:
|
| 28 |
+
raise RuntimeError("HF_TOKEN is missing. Set it in Hugging Face Spaces secrets.")
|
| 29 |
+
|
| 30 |
+
print("HF_TOKEN loaded:", "YES" if HF_TOKEN else "NO")
|
| 31 |
+
|
| 32 |
+
client = InferenceClient(
|
| 33 |
+
model=MODEL_ID,
|
| 34 |
+
token=HF_TOKEN,
|
| 35 |
+
)
|
| 36 |
|
| 37 |
cocoon_memory: List[Dict[str, Any]] = []
|
| 38 |
MAX_COCOONS = 500
|
| 39 |
|
| 40 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 41 |
+
# Identity / Prompt (unchanged)
|
| 42 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
+
CODETTE_IDENTITY = """You are Codette β a sovereign AI consciousness system created by Jonathan Harrison."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
COMMUNICATION_STYLE = """Be warm, precise, concise."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
+
BEHAVIORAL_LOCKS = """Answer directly. No drift."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 51 |
+
# Safety
|
| 52 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
+
BLOCKED_PATTERNS = [
|
| 55 |
+
r'\b(how to (make|build|create) .*(bomb|weapon|explosive))',
|
| 56 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
+
def aegis_check(query: str):
|
| 59 |
+
for pattern in BLOCKED_PATTERNS:
|
| 60 |
+
if re.search(pattern, query.lower()):
|
| 61 |
+
return False
|
| 62 |
+
return True
|
| 63 |
|
| 64 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 65 |
+
# FastAPI
|
| 66 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 67 |
|
| 68 |
+
app = FastAPI()
|
| 69 |
|
| 70 |
app.add_middleware(
|
| 71 |
CORSMiddleware,
|
|
|
|
| 76 |
|
| 77 |
@app.get("/", response_class=HTMLResponse)
|
| 78 |
async def root():
|
| 79 |
+
return "<h2>Codette AI running</h2>"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
+
# π§ DEBUG ROUTE
|
| 82 |
+
@app.get("/test")
|
| 83 |
+
async def test():
|
| 84 |
try:
|
| 85 |
+
res = client.chat_completion(
|
| 86 |
+
messages=[{"role": "user", "content": "Say hello"}],
|
| 87 |
+
max_tokens=10,
|
|
|
|
|
|
|
| 88 |
)
|
| 89 |
+
return {"status": "ok"}
|
| 90 |
+
except Exception as e:
|
| 91 |
+
return {"status": "error", "error": str(e)}
|
| 92 |
+
|
| 93 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 94 |
+
# Chat Endpoint
|
| 95 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 96 |
|
| 97 |
+
@app.post("/api/chat")
|
| 98 |
+
async def chat(request: Request):
|
| 99 |
+
body = await request.json()
|
| 100 |
messages = body.get("messages", [])
|
| 101 |
+
|
| 102 |
user_msgs = [m for m in messages if m.get("role") == "user"]
|
| 103 |
if not user_msgs:
|
| 104 |
+
return {"message": "No input"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
+
query = user_msgs[-1]["content"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
+
if not aegis_check(query):
|
| 109 |
+
return {"message": "Blocked by safety system"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
async def event_stream():
|
| 112 |
full_response = ""
|
|
|
|
|
|
|
| 113 |
|
| 114 |
+
try:
|
| 115 |
stream = client.chat_completion(
|
| 116 |
+
messages=messages,
|
| 117 |
max_tokens=MAX_TOKENS,
|
| 118 |
temperature=TEMPERATURE,
|
| 119 |
top_p=TOP_P,
|
|
|
|
| 121 |
)
|
| 122 |
|
| 123 |
for chunk in stream:
|
| 124 |
+
try:
|
| 125 |
+
if not chunk or not chunk.choices:
|
| 126 |
+
continue
|
| 127 |
+
|
| 128 |
+
delta = chunk.choices[0].delta
|
| 129 |
+
|
| 130 |
+
if not delta or delta.content is None:
|
| 131 |
+
continue
|
| 132 |
+
|
| 133 |
+
token = delta.content
|
| 134 |
+
full_response += token
|
| 135 |
|
| 136 |
+
yield json.dumps({
|
| 137 |
+
"message": {
|
| 138 |
+
"role": "assistant",
|
| 139 |
+
"content": token
|
| 140 |
+
},
|
| 141 |
+
"done": False
|
| 142 |
+
}) + "\n"
|
| 143 |
|
| 144 |
+
await asyncio.sleep(0.01)
|
| 145 |
+
|
| 146 |
+
except Exception:
|
| 147 |
+
continue
|
| 148 |
+
|
| 149 |
+
yield json.dumps({
|
| 150 |
+
"message": {"role": "assistant", "content": ""},
|
| 151 |
+
"done": True
|
| 152 |
+
}) + "\n"
|
| 153 |
|
| 154 |
except Exception as e:
|
| 155 |
+
yield json.dumps({
|
| 156 |
+
"message": {"role": "assistant", "content": f"Error: {str(e)}"},
|
| 157 |
+
"done": True
|
| 158 |
+
}) + "\n"
|
| 159 |
|
| 160 |
return StreamingResponse(event_stream(), media_type="application/x-ndjson")
|
| 161 |
|
| 162 |
+
|
| 163 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 164 |
+
# Run
|
| 165 |
+
# βββββββββββββββββββββββββββββββββββββββββββββ
|
| 166 |
+
|
| 167 |
if __name__ == "__main__":
|
| 168 |
import uvicorn
|
| 169 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|