Spaces:
Runtime error
Runtime error
Upload 8 files
Browse files- README.md +3 -3
- package-lock.json +43 -0
- package.json +14 -0
- requirements.txt +3 -0
- server.py +199 -0
- tokenizer.js +9 -0
README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
---
|
2 |
title: Saya
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
|
|
1 |
---
|
2 |
title: Saya
|
3 |
+
emoji: 🍃
|
4 |
+
colorFrom: green
|
5 |
+
colorTo: gray
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
package-lock.json
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "a server",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"lockfileVersion": 3,
|
5 |
+
"requires": true,
|
6 |
+
"packages": {
|
7 |
+
"": {
|
8 |
+
"name": "a server",
|
9 |
+
"version": "1.0.0",
|
10 |
+
"license": "ISC",
|
11 |
+
"dependencies": {
|
12 |
+
"@anthropic-ai/tokenizer": "^0.0.4"
|
13 |
+
}
|
14 |
+
},
|
15 |
+
"node_modules/@anthropic-ai/tokenizer": {
|
16 |
+
"version": "0.0.4",
|
17 |
+
"resolved": "https://registry.npmjs.org/@anthropic-ai/tokenizer/-/tokenizer-0.0.4.tgz",
|
18 |
+
"integrity": "sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g==",
|
19 |
+
"dependencies": {
|
20 |
+
"@types/node": "^18.11.18",
|
21 |
+
"tiktoken": "^1.0.10"
|
22 |
+
}
|
23 |
+
},
|
24 |
+
"node_modules/@types/node": {
|
25 |
+
"version": "18.19.30",
|
26 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.30.tgz",
|
27 |
+
"integrity": "sha512-453z1zPuJLVDbyahaa1sSD5C2sht6ZpHp5rgJNs+H8YGqhluCXcuOUmBYsAo0Tos0cHySJ3lVUGbGgLlqIkpyg==",
|
28 |
+
"dependencies": {
|
29 |
+
"undici-types": "~5.26.4"
|
30 |
+
}
|
31 |
+
},
|
32 |
+
"node_modules/tiktoken": {
|
33 |
+
"version": "1.0.13",
|
34 |
+
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.13.tgz",
|
35 |
+
"integrity": "sha512-JaL9ZnvTbGFMDIBeGdVkLt4qWTeCPw+n7Ock+wceAGRenuHA6nOOvMJFliNDyXsjg2osGKJWsXtO2xc74VxyDw=="
|
36 |
+
},
|
37 |
+
"node_modules/undici-types": {
|
38 |
+
"version": "5.26.5",
|
39 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
40 |
+
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
41 |
+
}
|
42 |
+
}
|
43 |
+
}
|
package.json
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "a server",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "A server",
|
5 |
+
"main": "tokenizer.js",
|
6 |
+
"dependencies": {
|
7 |
+
"@anthropic-ai/tokenizer": "^0.0.4"
|
8 |
+
},
|
9 |
+
"scripts": {
|
10 |
+
"start": "node tokenizer.js"
|
11 |
+
},
|
12 |
+
"author": "",
|
13 |
+
"license": "ISC"
|
14 |
+
}
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
Flask==2.1.2
|
2 |
+
Werkzeug==2.1.1
|
3 |
+
requests==2.27.1
|
server.py
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
from flask import Flask, request, Response, redirect, jsonify, stream_with_context
|
3 |
+
import random, subprocess, string, json
|
4 |
+
import os
|
5 |
+
import logging
|
6 |
+
from logging import NullHandler
|
7 |
+
import subprocess
|
8 |
+
from functools import wrap
|
9 |
+
|
10 |
+
# Copied pepsi (thanks <3) made some modification so it works for me :v hue hue, nah doesn't use that shitty api thingy :v world sim whatever >.>
|
11 |
+
|
12 |
+
def validate_token(token):
|
13 |
+
if token == os.environ.get('PASSWORD'):
|
14 |
+
return True
|
15 |
+
else:
|
16 |
+
return False
|
17 |
+
|
18 |
+
def requires_auth_bearer(f):
|
19 |
+
@wraps(f)
|
20 |
+
def decorated(*args, **kwargs):
|
21 |
+
if 'X-Api-Key' not in request.headers:
|
22 |
+
return jsonify({'error': 'You need special Saya password :3c'}), 401
|
23 |
+
token = request.headers['X-Api-Key']
|
24 |
+
if not validate_token(token):
|
25 |
+
return jsonify({'error': 'hue hue hue hue hue hue '}), 401
|
26 |
+
return f(*args, **kwargs)
|
27 |
+
return decorated
|
28 |
+
|
29 |
+
app = Flask(__name__)
|
30 |
+
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
|
31 |
+
|
32 |
+
state = {
|
33 |
+
"current_requests": 0,
|
34 |
+
"total_prompts_sent": 0,
|
35 |
+
"total_tokens": 0,
|
36 |
+
"logging_enabled": os.environ.get('LOGGING_ENABLED', 'False').lower() in ['true', '1', 't']
|
37 |
+
}
|
38 |
+
|
39 |
+
## ill ne ver turn on logging i just wanted to show its off on page (same pepsi same :V)
|
40 |
+
def configure_logging():
|
41 |
+
app.logger.addHandler(NullHandler())
|
42 |
+
app.logger.propagate = False
|
43 |
+
bloody_log = logging.getLogger('bloody')
|
44 |
+
bloody_log.setLevel(logging.ERROR)
|
45 |
+
bloody_log.addHandler(NullHandler())
|
46 |
+
|
47 |
+
configure_logging()
|
48 |
+
|
49 |
+
def get_token_count(text):
|
50 |
+
result = subprocess.run(['node', 'tokenizer.js', text], capture_output=True, text=True)
|
51 |
+
return int(result.stdout.strip())
|
52 |
+
|
53 |
+
def generate_message_id():
|
54 |
+
chars = string.ascii_uppercase + string.digits + string.ascii_lowercase
|
55 |
+
return 'msg_' + ''.join(random.choices(chars, k=16))
|
56 |
+
|
57 |
+
|
58 |
+
@app.errorhandler(404)
|
59 |
+
def page_not_found(e):
|
60 |
+
return redirect(random.choice([
|
61 |
+
'https://youtu.be/0o0y-MNqdQU',
|
62 |
+
'https://youtu.be/oAJC1Pn78ZA',
|
63 |
+
'https://youtu.be/p_5tTM9D7l0',
|
64 |
+
'https://youtu.be/H0gEjUEneBI',
|
65 |
+
'https://youtu.be/Hj8icpQldzc',
|
66 |
+
'https://youtu.be/-9_sTTYXcwc',
|
67 |
+
'https://youtu.be/LmsuxO5rfEU',
|
68 |
+
'https://youtu.be/VJkzfV7kNYQ',
|
69 |
+
'https://youtu.be/oCikD1xcv0o',
|
70 |
+
'https://youtu.be/k9TSVx9gAW0',
|
71 |
+
'https://youtu.be/Xiiy8vEWj-g',
|
72 |
+
'https://youtu.be/FKLd1YdmIwA',
|
73 |
+
'https://youtu.be/RJ4iaQAF6SI',
|
74 |
+
'https://youtu.be/KPad2ftEwqc'
|
75 |
+
])), 302
|
76 |
+
|
77 |
+
@app.route('/saya/messages', methods=['POST'])
|
78 |
+
@requires_auth_bearer
|
79 |
+
def proxy_message():
|
80 |
+
state["total_prompts_sent"] += 1
|
81 |
+
state["current_requests"] += 1
|
82 |
+
|
83 |
+
data = request.get_json()
|
84 |
+
if not data or 'messages' not in data:
|
85 |
+
abort(400, 'Bad Request: No messages found in the request.')
|
86 |
+
full_text = ' '.join(msg.get('content', '') for msg in data['messages'])
|
87 |
+
token_count = get_token_count(full_text)
|
88 |
+
|
89 |
+
data["model"] = os.environ.get('MODEL')
|
90 |
+
|
91 |
+
headers = {
|
92 |
+
"Authorization": f"Bearer {os.environ.get('KEY')}",
|
93 |
+
'Content-Type': 'application/json',
|
94 |
+
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/13.2b11866 Mobile/16A366 Safari/605.1.15',
|
95 |
+
'Origin': os.environ.get('ORIGIN'),
|
96 |
+
'Referer': os.environ.get('REFERER'),
|
97 |
+
"Accept": "*/*",
|
98 |
+
"Accept-Language": "en-US,en;q=0.5",
|
99 |
+
"Sec-Fetch-Dest": "empty",
|
100 |
+
"Sec-Fetch-Mode": "cors",
|
101 |
+
"Sec-Fetch-Site": "same-origin",
|
102 |
+
"Sec-GPC": "1"
|
103 |
+
}
|
104 |
+
proxied_url = os.environ.get('PROXIED_URL')
|
105 |
+
response = requests.post(proxied_url, headers=headers, data=json.dumps(data), stream=True)
|
106 |
+
|
107 |
+
if not data.get('stream', False):
|
108 |
+
output = ''.join(chunk.decode('utf-8') for chunk in response.iter_content(chunk_size=1024) if chunk)
|
109 |
+
response_json = {
|
110 |
+
"content": [{"text": output, "type": "text"}],
|
111 |
+
"id": generate_message_id(),
|
112 |
+
"model": ":^)",
|
113 |
+
"role": "assistant",
|
114 |
+
"stop_reason": "end_turn",
|
115 |
+
"stop_sequence": None,
|
116 |
+
"type": "message",
|
117 |
+
"usage": {
|
118 |
+
"input_tokens": token_count,
|
119 |
+
"output_tokens": 25,
|
120 |
+
}
|
121 |
+
}
|
122 |
+
state["current_requests"] -= 1
|
123 |
+
return Response(json.dumps(response_json), headers={'Content-Type': 'application/json'}, status=response.status_code)
|
124 |
+
|
125 |
+
@stream_with_context
|
126 |
+
def generate_stream():
|
127 |
+
tokens_sent = 0
|
128 |
+
text_buffer = ''
|
129 |
+
try:
|
130 |
+
yield 'event: message_start\n'
|
131 |
+
yield f'data: {{"type": "message_start", "message": {{"id": "{generate_message_id()}", "type": "message", "role": "assistant", "content": [], "model": "claude-3-opus-20240229", "stop_reason": null, "stop_sequence": null, "usage": {{"input_tokens": 25, "output_tokens": 1}}}}}}\n\n'
|
132 |
+
yield 'event: content_block_start\n'
|
133 |
+
yield 'data: {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}\n\n'
|
134 |
+
incomplete_chunk=""
|
135 |
+
for chunk in response.iter_content(chunk_size=256):
|
136 |
+
if chunk:
|
137 |
+
decoded_chunk = incomplete_chunk+chunk.decode('utf-8', 'xmlcharrefreplace')
|
138 |
+
if "data:" in decoded_chunk:
|
139 |
+
if 'null}]}' not in decoded_chunk:
|
140 |
+
incomplete_chunk+= decoded_chunk
|
141 |
+
else:
|
142 |
+
for chunkparts in decoded_chunk.split("\n"):
|
143 |
+
if "data:" in chunkparts and "null}]}" in chunkparts:
|
144 |
+
data = json.loads(chunkparts.replace("data: ",""))
|
145 |
+
try:
|
146 |
+
text_buffer += data["choices"][0]["delta"]["content"]
|
147 |
+
data_dump = json.dumps({"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": data["choices"][0]["delta"]["content"]}})
|
148 |
+
yield f'event: content_block_delta\n'
|
149 |
+
yield f'data: {data_dump}\n\n'
|
150 |
+
|
151 |
+
|
152 |
+
if 'content_block_stop' in chunkparts:
|
153 |
+
tokens_sent += get_token_count(text_buffer)
|
154 |
+
text_buffer = ''
|
155 |
+
incomplete_chunk=""
|
156 |
+
except:
|
157 |
+
pass
|
158 |
+
else:
|
159 |
+
incomplete_chunk=chunkparts
|
160 |
+
else:
|
161 |
+
if decoded_chunk[0] != ":":
|
162 |
+
incomplete_chunk+=decoded_chunk
|
163 |
+
|
164 |
+
yield 'event: content_block_stop\n'
|
165 |
+
yield 'data: {"type": "content_block_stop", "index": 0}\n\n'
|
166 |
+
yield 'event: message_delta\n'
|
167 |
+
yield 'data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence":null, "usage":{"output_tokens": 15}}}\n\n'
|
168 |
+
yield 'event: message_stop\n'
|
169 |
+
yield 'data: {"type": "message_stop"}\n\n'
|
170 |
+
except GeneratorExit:
|
171 |
+
pass
|
172 |
+
finally:
|
173 |
+
if text_buffer:
|
174 |
+
tokens_sent += get_token_count(text_buffer)
|
175 |
+
state["current_requests"] -= 1
|
176 |
+
state["total_tokens"] += tokens_sent
|
177 |
+
|
178 |
+
return Response(generate_stream(), headers={'Content-Type': 'text/event-stream'})
|
179 |
+
|
180 |
+
@app.route('/')
|
181 |
+
def index():
|
182 |
+
space_host = os.environ.get('SPACE_HOST', 'default-space-host')
|
183 |
+
endpoint = f"https://{space_host}/saya"
|
184 |
+
payload = '''<body style="background-image: url('https://images.dragonetwork.pl/wp12317828.jpg'); background-size: cover; background-repeat: no-repeat;">
|
185 |
+
<div style="background-color: rgba(0, 0, 0, 0.5); padding: 20px;">
|
186 |
+
<p style="color: white; font-weight: bold; font-size: 200%;">
|
187 |
+
<span>Endpoint:</span> <span>''' + endpoint + '''</span><br>
|
188 |
+
<span>Prompt Logging:</span> <span>''' + str(state["logging_enabled"]) + '''</span><br>
|
189 |
+
<span>Prompters Now:</span> <span>''' + str(state["current_requests"]) + '''</span><br>
|
190 |
+
<span>Total Prompts:</span> <span>''' + str(state["total_prompts_sent"]) + '''</span><br>
|
191 |
+
<span>Total Tokens:</span> <span>''' + str(state["total_tokens"]) + '''</span><br>
|
192 |
+
<span>Password Protected?:</span> <span>yes :3c</span><br>
|
193 |
+
</p>
|
194 |
+
</div>
|
195 |
+
</body>'''
|
196 |
+
return payload, 200
|
197 |
+
|
198 |
+
if __name__ == '__main__':
|
199 |
+
app.run(host='0.0.0.0', port=7860, debug=False)
|
tokenizer.js
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { getTokenizer } = require('@anthropic-ai/tokenizer');
|
2 |
+
const tokenizer = getTokenizer();
|
3 |
+
function getTokenCount(text) {
|
4 |
+
const tokens = tokenizer.encode(text.normalize('NFKC'), 'all');
|
5 |
+
return tokens.length;
|
6 |
+
}
|
7 |
+
const text = process.argv[2];
|
8 |
+
const tokenCount = getTokenCount(text);
|
9 |
+
console.log(tokenCount);
|