twgood commited on
Commit
f0a85aa
·
verified ·
1 Parent(s): eeb8b99
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. app.py +204 -0
  3. config.json +8 -0
  4. russian.txt +3 -0
  5. templates/index.html +98 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ russian.txt filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, abort, render_template, g
2
+ from flask_cors import CORS
3
+ import sqlite3, json, random, uuid, datetime, os
4
+
5
+ app = Flask(__name__)
6
+ CORS(app)
7
+
8
+ DB_PATH = os.path.abspath("data.db")
9
+ CONFIG = json.load(open("config.json", encoding="utf-8"))
10
+
11
+ # ---------- DB ----------
12
+ def get_db():
13
+ if "db" not in g:
14
+ g.db = sqlite3.connect(
15
+ DB_PATH,
16
+ timeout=10,
17
+ check_same_thread=False
18
+ )
19
+ g.db.execute("PRAGMA journal_mode=WAL;")
20
+ g.db.execute("PRAGMA synchronous=NORMAL;")
21
+ return g.db
22
+
23
+ @app.teardown_appcontext
24
+ def close_db(e=None):
25
+ db = g.pop("db", None)
26
+ if db:
27
+ db.close()
28
+
29
+ def init_db():
30
+ with sqlite3.connect(DB_PATH) as con:
31
+ con.execute("""
32
+ CREATE TABLE IF NOT EXISTS api_keys (
33
+ key TEXT PRIMARY KEY,
34
+ requests INTEGER DEFAULT 0,
35
+ last_day TEXT
36
+ )""")
37
+ con.execute("""
38
+ CREATE TABLE IF NOT EXISTS messages (
39
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
40
+ api_key TEXT,
41
+ role TEXT,
42
+ content TEXT,
43
+ ts DATETIME DEFAULT CURRENT_TIMESTAMP
44
+ )""")
45
+ con.commit()
46
+
47
+ init_db()
48
+
49
+ # ---------- WORDS ----------
50
+ def load_words():
51
+ for enc in ("utf-8", "cp1251"):
52
+ try:
53
+ with open("russian.txt", encoding=enc) as f:
54
+ return [w.strip() for w in f if w.strip()]
55
+ except:
56
+ pass
57
+ return ["я", "думаю"]
58
+
59
+ WORDS = load_words()
60
+
61
+ TEMPLATES = [
62
+ "я думаю что {w} {w}",
63
+ "{w} это {w}",
64
+ "иногда {w}, но {w}",
65
+ "мне кажется {w}"
66
+ ]
67
+
68
+ # ---------- AI ----------
69
+ def sentence():
70
+ tpl = random.choice(TEMPLATES)
71
+ for _ in range(tpl.count("{w}")):
72
+ tpl = tpl.replace("{w}", random.choice(WORDS), 1)
73
+ return tpl.capitalize() + random.choice(CONFIG["endings"])
74
+
75
+ def generate_answer():
76
+ n = random.randint(CONFIG["min_sentences"], CONFIG["max_sentences"])
77
+ return " ".join(sentence() for _ in range(n))
78
+
79
+ # ---------- AUTH ----------
80
+ def get_api_key():
81
+ h = request.headers.get("Authorization", "")
82
+ if h.startswith("Bearer "):
83
+ return h[7:]
84
+ return request.headers.get("X-API-Key")
85
+
86
+ def check_key():
87
+ key = get_api_key()
88
+ if not key:
89
+ abort(401, "API key required")
90
+
91
+ db = get_db()
92
+ cur = db.cursor()
93
+
94
+ row = cur.execute(
95
+ "SELECT requests, last_day FROM api_keys WHERE key=?",
96
+ (key,)
97
+ ).fetchone()
98
+
99
+ if not row:
100
+ abort(403, "Invalid API key")
101
+
102
+ today = str(datetime.date.today())
103
+ requests, last_day = row
104
+
105
+ if last_day != today:
106
+ requests = 0
107
+
108
+ if requests >= CONFIG["daily_limit"]:
109
+ abort(429, "Daily limit exceeded")
110
+
111
+ cur.execute(
112
+ "UPDATE api_keys SET requests=?, last_day=? WHERE key=?",
113
+ (requests + 1, today, key)
114
+ )
115
+ db.commit()
116
+ return key
117
+
118
+ # ---------- ROUTES ----------
119
+ @app.route("/")
120
+ def index():
121
+ return render_template("index.html")
122
+
123
+ @app.route("/health")
124
+ def health():
125
+ return jsonify({"status": "ok", "version": "1.0.0"})
126
+
127
+ @app.route("/config")
128
+ def get_config():
129
+ return jsonify(CONFIG)
130
+
131
+ @app.route("/v1/keys", methods=["POST"])
132
+ def create_key():
133
+ key = uuid.uuid4().hex
134
+ db = get_db()
135
+ db.execute(
136
+ "INSERT INTO api_keys(key, requests, last_day) VALUES (?,0,'')",
137
+ (key,)
138
+ )
139
+ db.commit()
140
+ return jsonify({"api_key": key})
141
+
142
+ @app.route("/v1/chat", methods=["POST"])
143
+ def chat():
144
+ key = check_key()
145
+
146
+ data = request.get_json(silent=True) or request.form
147
+ msg = data.get("message", "")
148
+ if not msg:
149
+ return jsonify({"error": "message required"}), 400
150
+
151
+ db = get_db()
152
+ db.execute(
153
+ "INSERT INTO messages(api_key, role, content) VALUES (?,?,?)",
154
+ (key, "user", msg)
155
+ )
156
+
157
+ answer = generate_answer()
158
+
159
+ db.execute(
160
+ "INSERT INTO messages(api_key, role, content) VALUES (?,?,?)",
161
+ (key, "assistant", answer)
162
+ )
163
+ db.commit()
164
+
165
+ return jsonify({"answer": answer})
166
+
167
+ @app.route("/v1/chat/completions", methods=["POST"])
168
+ def openai_style():
169
+ key = check_key()
170
+ data = request.get_json(silent=True) or {}
171
+
172
+ messages = data.get("messages", [])
173
+ user_msg = ""
174
+ for m in reversed(messages):
175
+ if m.get("role") == "user":
176
+ user_msg = m.get("content", "")
177
+ break
178
+
179
+ answer = generate_answer()
180
+
181
+ return jsonify({
182
+ "id": "chatcmpl-local",
183
+ "object": "chat.completion",
184
+ "choices": [{
185
+ "index": 0,
186
+ "message": {
187
+ "role": "assistant",
188
+ "content": answer
189
+ },
190
+ "finish_reason": "stop"
191
+ }]
192
+ })
193
+
194
+ @app.route("/v1/history")
195
+ def history():
196
+ key = check_key()
197
+ rows = get_db().execute(
198
+ "SELECT role, content FROM messages WHERE api_key=? ORDER BY id",
199
+ (key,)
200
+ ).fetchall()
201
+ return jsonify(rows)
202
+
203
+ if __name__ == "__main__":
204
+ app.run(host="0.0.0.0", port=5000, debug=False)
config.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "min_sentences": 1,
3
+ "max_sentences": 3,
4
+ "typing_speed": 25,
5
+ "memory_limit": 6,
6
+ "daily_limit": 100,
7
+ "endings": [".", "...", "!", "?!"]
8
+ }
russian.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e898af1bd42ca7be38a28068087be09429b677044b3b6cbdccc4878da67adb90
3
+ size 35010803
templates/index.html ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="ru">
3
+ <meta charset="utf-8">
4
+ <title>EBLAN GPT</title>
5
+ <style>
6
+ body{margin:0;background:#0e0e0e;color:#eee;font-family:system-ui}
7
+ .app{max-width:900px;margin:auto;height:100vh;display:flex;flex-direction:column}
8
+ .top{display:flex;gap:8px;padding:10px;border-bottom:1px solid #222}
9
+ input,button{background:#111;color:#fff;border:1px solid #333;border-radius:6px;padding:8px}
10
+ .msgs{flex:1;overflow-y:auto;padding:20px}
11
+ .msg{max-width:70%;margin-bottom:10px;padding:12px;border-radius:10px}
12
+ .user{background:#1f6feb;margin-left:auto}
13
+ .bot{background:#222}
14
+ .bottom{display:flex;gap:8px;padding:10px;border-top:1px solid #222}
15
+ </style>
16
+
17
+ <div class="app">
18
+ <div class="top">
19
+ <b>🍅 EBLAN GPT</b>
20
+ <input id="key" placeholder="API key">
21
+ <button onclick="newKey()">+key</button>
22
+ <button onclick="loadHistory()">history</button>
23
+ <button onclick="clearChat()">clear</button>
24
+ </div>
25
+
26
+ <div class="msgs" id="msgs"></div>
27
+
28
+ <div class="bottom">
29
+ <input id="input" style="flex:1" placeholder="сообщение">
30
+ <button onclick="send()">▶</button>
31
+ </div>
32
+ </div>
33
+
34
+ <script>
35
+ let SPEED=25;
36
+ fetch("/config").then(r=>r.json()).then(c=>SPEED=c.typing_speed);
37
+
38
+ const msgs=document.getElementById("msgs");
39
+ const input=document.getElementById("input");
40
+ const key=document.getElementById("key");
41
+
42
+ function add(cls,t=""){
43
+ const d=document.createElement("div");
44
+ d.className="msg "+cls;
45
+ d.textContent=t;
46
+ msgs.appendChild(d);
47
+ msgs.scrollTop=msgs.scrollHeight;
48
+ return d;
49
+ }
50
+
51
+ function type(el,text){
52
+ let i=0;
53
+ const t=setInterval(()=>{
54
+ el.textContent+=text[i++];
55
+ if(i>=text.length) clearInterval(t);
56
+ },SPEED);
57
+ }
58
+
59
+ async function send(){
60
+ if(!key.value) return alert("API key нужен");
61
+ const msg=input.value.trim();
62
+ if(!msg) return;
63
+ add("user",msg);
64
+ input.value="";
65
+
66
+ const r=await fetch("/v1/chat",{
67
+ method:"POST",
68
+ headers:{
69
+ "Content-Type":"application/json",
70
+ "X-API-Key":key.value
71
+ },
72
+ body:JSON.stringify({message:msg})
73
+ });
74
+
75
+ const d=await r.json();
76
+ const b=add("bot");
77
+ type(b,d.answer);
78
+ }
79
+
80
+ function newKey(){
81
+ fetch("/v1/keys",{method:"POST"})
82
+ .then(r=>r.json())
83
+ .then(d=>key.value=d.api_key);
84
+ }
85
+
86
+ function loadHistory(){
87
+ fetch("/v1/history",{headers:{"X-API-Key":key.value}})
88
+ .then(r=>r.json())
89
+ .then(h=>{
90
+ msgs.innerHTML="";
91
+ h.forEach(x=>add(x[0]==="user"?"user":"bot",x[1]));
92
+ });
93
+ }
94
+
95
+ function clearChat(){msgs.innerHTML=""}
96
+ input.onkeydown=e=>{if(e.key==="Enter")send()}
97
+ </script>
98
+ </html>