ffreemt commited on
Commit
a4924d0
·
1 Parent(s): 80fc0ac

Update Dockfile, modify app.py

Browse files
Files changed (4) hide show
  1. Dockerfile +10 -5
  2. README.md +1 -1
  3. app.py +206 -0
  4. requirements.txt +3 -0
Dockerfile CHANGED
@@ -1,8 +1,13 @@
1
  FROM python:3.11
2
- RUN apt update
3
- RUN apt install git
4
- RUN git clone https://github.com/BlackNum/reka.git /app
 
 
5
  WORKDIR /app
6
- RUN pip install -r requirements.txt
 
 
 
7
  EXPOSE 7860
8
- CMD ["python", "main.py"]
 
1
  FROM python:3.11
2
+ # RUN git clone https://github.com/BlackNum/reka.git /app
3
+
4
+ ENV PIP_ROOT_USER_ACTION=ignore TZ=Asia/Shanghai
5
+ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
6
+
7
  WORKDIR /app
8
+ COPY . .
9
+
10
+ RUN pip install --no-cache-dir --upgrade pip && \
11
+ pip install --no-cache-dir -r requirements.txt
12
  EXPOSE 7860
13
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -4,7 +4,7 @@ emoji: 🏢
4
  colorFrom: yellow
5
  colorTo: yellow
6
  sdk: docker
7
- pinned: false
8
  license: apache-2.0
9
  ---
10
 
 
4
  colorFrom: yellow
5
  colorTo: yellow
6
  sdk: docker
7
+ pinned: true
8
  license: apache-2.0
9
  ---
10
 
app.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import time
3
+ import requests
4
+ from flask import Flask, request, Response, stream_with_context
5
+ from bs4 import BeautifulSoup
6
+ from datetime import datetime, timedelta
7
+ import logging
8
+ import os
9
+
10
+ from ycecream import y
11
+ y.configure(sln=1)
12
+
13
+ app = Flask(__name__)
14
+
15
+ APP_SESSION_VALIDITY = timedelta(days=3)
16
+ ACCESS_TOKEN_VALIDITY = timedelta(hours=1)
17
+ USERNAME = os.environ.get('USERNAME', '')
18
+ PASSWORD = os.environ.get('PASSWORD', '')
19
+ AUTHKEY = os.environ.get('AUTHKEY', '')
20
+
21
+ y(USERNAME, PASSWORD, AUTHKEY)
22
+
23
+ cache = {"app_session": None, "app_session_time": None, "access_token": None, "access_token_time": None}
24
+
25
+ y(cache)
26
+
27
+ # 配置日志记录
28
+ logging.basicConfig(level=logging.DEBUG)
29
+
30
+ def fetch_tokens():
31
+ session = requests.Session()
32
+
33
+ # 检查并获取 appSession
34
+ if not cache["app_session_time"] or datetime.now() - cache["app_session_time"] >= APP_SESSION_VALIDITY:
35
+ logging.info("Fetching new appSession")
36
+ login_page_response = session.get('https://chat.reka.ai/bff/auth/login', allow_redirects=True)
37
+ if login_page_response.status_code != 200:
38
+ logging.error("Failed to load login page")
39
+ return None
40
+ soup = BeautifulSoup(login_page_response.text, 'html.parser')
41
+ state_value = soup.find('input', {'name': 'state'})['value']
42
+ session.post(f"https://auth.reka.ai/u/login?state={state_value}", data={
43
+ 'state': state_value, 'username': USERNAME, 'password': PASSWORD, 'action': 'default'
44
+ })
45
+ cache["app_session"] = session.cookies.get('appSession')
46
+ cache["app_session_time"] = datetime.now()
47
+
48
+ # 检查并获取 accessToken
49
+ if not cache["access_token_time"] or datetime.now() - cache["access_token_time"] >= ACCESS_TOKEN_VALIDITY:
50
+ logging.info("Fetching new accessToken")
51
+ response = session.get('https://chat.reka.ai/bff/auth/access_token', headers={
52
+ 'Cookie': f'appSession={cache["app_session"]}'
53
+ })
54
+ if response.status_code != 200:
55
+ logging.error("Failed to get access token")
56
+ return None
57
+ cache["access_token"] = response.json().get('accessToken')
58
+ cache["access_token_time"] = datetime.now()
59
+
60
+ y(cache)
61
+
62
+ return cache["access_token"]
63
+
64
+ @app.route('/')
65
+ def landing():
66
+ return """
67
+ query /hf/v1/chat/completions for a spin, e.g. curl -XPOST 127.0.0.1:7860/hf/v1/chat/completions -H "Authorization: Bearer Your_AUTHKEY"
68
+
69
+ or
70
+ curl -XPOST [127.0.0.1:7860|hf space url]/hf/v1/chat/completions -H "Authorization: Bearer Your_AUTHKEY" -H "Content-Type: application/json" --data "{\"model\": \"gpt-3.5-turbo\", \"messages\": [{\"role\": \"user\", \"content\": \"Say this is a test!\"}]}"
71
+
72
+ """
73
+
74
+ @app.route('/hf/v1/chat/completions', methods=['POST', 'OPTIONS'])
75
+ def chat_completions():
76
+ if request.method == "OPTIONS":
77
+ return Response("", status=204, headers={
78
+ 'Access-Control-Allow-Origin': '*',
79
+ 'Access-Control-Allow-Headers': '*'
80
+ })
81
+
82
+ if request.method != 'POST' or request.path != '/hf/v1/chat/completions' or request.headers.get('Authorization') != f'Bearer {AUTHKEY}':
83
+ logging.error("Unauthorized access attempt")
84
+ return Response('Unauthorized', status=401)
85
+
86
+ access_token = fetch_tokens()
87
+ if not access_token:
88
+ logging.error("Failed to obtain access token")
89
+ return Response("Failed to obtain access token.", status=500)
90
+
91
+ try:
92
+ request_body = request.json
93
+ except Exception as e:
94
+ logging.error(f"Error parsing JSON body: {e}")
95
+ return Response("Error parsing JSON body", status=400)
96
+
97
+ messages = request_body.get("messages", [])
98
+ model = request_body.get("model", "reka-core")
99
+
100
+ conversation_history = [{"type": "human" if msg["role"] in ["user", "system"] else "model", "text": msg["content"]} for msg in messages]
101
+
102
+ if conversation_history and conversation_history[0]["type"] != "human":
103
+ conversation_history.insert(0, {"type": "human", "text": ""})
104
+ if conversation_history and conversation_history[-1]["type"] != "human":
105
+ conversation_history.append({"type": "human", "text": ""})
106
+
107
+ i = 0
108
+ while i < len(conversation_history) - 1:
109
+ if conversation_history[i]["type"] == conversation_history[i + 1]["type"]:
110
+ conversation_history.insert(i + 1, {"type": "model" if conversation_history[i]["type"] == "human" else "human", "text": ""})
111
+ i += 1
112
+
113
+ new_request_body = {
114
+ "conversation_history": conversation_history,
115
+ "stream": True,
116
+ "use_search_engine": False,
117
+ "use_code_interpreter": False,
118
+ "model_name": "reka-core",
119
+ "random_seed": int(time.time())
120
+ }
121
+
122
+ response = requests.post(
123
+ "https://chat.reka.ai/api/chat",
124
+ headers={
125
+ "authorization": f"bearer {access_token}",
126
+ "content-type": "application/json"
127
+ },
128
+ data=json.dumps(new_request_body),
129
+ stream=True
130
+ )
131
+
132
+ if response.status_code != 200:
133
+ logging.error(f"Error from external API: {response.status_code} {response.text}")
134
+ return Response(response.text, status=response.status_code)
135
+
136
+ created = int(time.time())
137
+
138
+ def generate_stream():
139
+ decoder = json.JSONDecoder()
140
+ encoder = json.JSONEncoder()
141
+ content_buffer = ""
142
+ full_content = ""
143
+ prev_content = ""
144
+ last_four_texts = []
145
+
146
+ for line in response.iter_lines():
147
+ if line:
148
+ content_buffer += line.decode('utf-8') + "\n"
149
+ while "\n" in content_buffer:
150
+ newline_index = content_buffer.index("\n")
151
+ line = content_buffer[:newline_index]
152
+ content_buffer = content_buffer[newline_index + 1:]
153
+
154
+ if not line.startswith("data:"):
155
+ continue
156
+
157
+ try:
158
+ data = decoder.decode(line[5:])
159
+ except json.JSONDecodeError:
160
+ continue
161
+
162
+ last_four_texts.append(data["text"])
163
+ if len(last_four_texts) > 4:
164
+ last_four_texts.pop(0)
165
+
166
+ if len(last_four_texts) == 4 and (
167
+ len(last_four_texts[3]) < len(last_four_texts[2])
168
+ or last_four_texts[3].endswith("<sep")
169
+ or last_four_texts[3].endswith("<")):
170
+ break
171
+
172
+ full_content = data["text"]
173
+ new_content = full_content[len(prev_content):]
174
+ prev_content = full_content
175
+
176
+ formatted_data = {
177
+ "id": "chatcmpl-" + "".join([str(time.time()), str(hash(new_content))]),
178
+ "object": "chat.completion.chunk",
179
+ "created": created,
180
+ "model": model,
181
+ "choices": [{
182
+ "index": 0,
183
+ "delta": {"content": new_content},
184
+ "finish_reason": None
185
+ }]
186
+ }
187
+ yield f"data: {encoder.encode(formatted_data)}\n\n"
188
+
189
+ done_data = {
190
+ "id": "chatcmpl-" + "".join([str(time.time()), str(hash("done"))]),
191
+ "object": "chat.completion.chunk",
192
+ "created": created,
193
+ "model": model,
194
+ "choices": [{
195
+ "index": 0,
196
+ "delta": {},
197
+ "finish_reason": "stop"
198
+ }]
199
+ }
200
+ yield f"data: {json.dumps(done_data)}\n\n"
201
+ yield "data: [DONE]\n\n"
202
+
203
+ return Response(stream_with_context(generate_stream()), headers={"Content-Type": "text/event-stream"})
204
+
205
+ if __name__ == '__main__':
206
+ app.run(host='0.0.0.0', port=7860)
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ Flask==3.0.3
2
+ requests==2.31.0
3
+ beautifulsoup4==4.12.3