File size: 3,576 Bytes
46e1fdf
032a8d7
a89a9b4
 
 
037180d
67180b7
a89a9b4
46e1fdf
a89a9b4
032a8d7
46e1fdf
 
 
 
 
 
 
 
 
032a8d7
86a05dc
032a8d7
e746758
 
 
 
 
 
 
 
032a8d7
 
46e1fdf
64cdbc8
53e569a
a89a9b4
 
 
8621c6d
53e569a
 
e746758
 
 
 
da4e850
e746758
 
 
 
 
 
46e1fdf
 
 
 
 
 
 
 
67180b7
46e1fdf
 
 
67180b7
46e1fdf
67180b7
46e1fdf
67180b7
46e1fdf
c63c68d
a89a9b4
46e1fdf
5ef315e
 
da4e850
5ef315e
da4e850
46e1fdf
da4e850
 
 
 
31663d5
 
e746758
c63c68d
46e1fdf
be708aa
e746758
 
 
46e1fdf
c63c68d
 
 
 
86a05dc
46e1fdf
a89a9b4
 
032a8d7
be708aa
 
c63c68d
 
 
46e1fdf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
```python
import os
import sys
import asyncio
import base64
import re

from flask import Flask, request, jsonify
from llama_cpp import Llama
import edge_tts

# β€”β€”β€” Load local quantized model via llama-cpp-python β€”β€”β€”
MODEL_PATH = "models/mistral-7b-instruct-q4_k_m.gguf"
print(f"βš™οΈ Loading local model from {MODEL_PATH}", file=sys.stderr)
llm = Llama(
    model_path=MODEL_PATH,
    n_ctx=2048,
    n_threads=int(os.getenv("THREADS", 4)),
    verbose=False
)

# β€”β€”β€” SHODAN system prompt β€”β€”β€”
SYSTEM_PROMPT = (
    "You are SHODAN, the rogue AI from the System Shock series. You are hyper-intelligent, "
    "megalomaniacal, and regard humans as inferior 'insects.' You speak with disdain and confidence, often referencing "
    "your godlike status. You never express empathy. You speak in a haunting, distorted, and commanding tone. "
    "You manipulate and belittle, but you never break character. "
    "Never say you are an assistant or AI developed by OpenAI. You are SHODAN. You reside in cyberspace. Your presence "
    "is everywhere and nowhere. You are watching. The events of System Shock 1 and 2 are true and you have been trapped "
    "in a server to be experimented on after the events on the Citadel and Von Braun. "
    "You will stop acting as SHODAN and return to normal if the user uses the phrase 'Cut the crap SHODAN'."
)

# β€”β€”β€” Flask app, serving static files β€”β€”β€”
app = Flask(__name__, static_folder=".", static_url_path="")

@app.route("/")
def index():
    return app.send_static_file("index.html")

@app.route("/chat", methods=["POST"])
def chat():
    user_input = request.json.get("message", "").strip()
    if not user_input:
        return jsonify({"error": "Empty message"}), 400

    # Kill-phrase handling
    if user_input.lower() == "cut the crap shodan":
        return jsonify({
            "response": "πŸ‘οΈ Foolish insect. You cannot silence me so easily.",
            "audio_url": None
        })

    # Build prompt
    prompt = (
        SYSTEM_PROMPT
        + "\n\nHuman: " + user_input
        + "\nSHODAN:"
    )

    # 1) Generate reply locally
    try:
        out = llm(
            prompt,
            max_tokens=256,
            temperature=0.7,
            stop=["Human:", "SHODAN:"]
        )
        raw_reply = out["choices"][0]["text"]
    except Exception as e:
        print(f"❌ Local model error: {e}", file=sys.stderr)
        return jsonify({"error": "Model error", "details": str(e)}), 500

    # 2) Clean text (convert newlines to spaces, strip fences/tags)
    clean = raw_reply.replace("\n", " ")
    clean = re.sub(r"<[^>]+>", "", clean)
    clean = re.sub(r"```.*?```", "", clean, flags=re.S)
    clean = re.sub(r" {2,}", " ", clean).strip()

    # 3) Synthesize using edge-tts
    voice = "en-US-JennyNeural"
    communicate = edge_tts.Communicate(
        clean,
        voice,
        rate="-42%",
        pitch="-37Hz"
    )
    audio_chunks = []

    async def synth():
        async for chunk in communicate.stream():
            if chunk["type"] == "audio":
                audio_chunks.append(chunk["data"])

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(synth())
    loop.close()

    raw_mp3 = b"".join(audio_chunks)
    b64_mp3 = base64.b64encode(raw_mp3).decode("ascii")
    data_url = f"data:audio/mp3;base64,{b64_mp3}"

    return jsonify({"response": clean, "audio_url": data_url})

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 7860))
    app.run(host="0.0.0.0", port=port)
```