Severian commited on
Commit
4b04903
·
verified ·
1 Parent(s): afaeb4d

Update server.py

Browse files
Files changed (1) hide show
  1. server.py +87 -200
server.py CHANGED
@@ -1,250 +1,137 @@
1
  import os
2
- from flask import Flask, request, jsonify, Response, stream_with_context
3
  import requests
4
  import logging
5
  from dotenv import load_dotenv
6
  import json
 
7
 
8
  # Load environment variables
9
  load_dotenv()
10
 
11
  app = Flask(__name__)
12
 
13
- ANTHROPIC_API_URL = os.getenv('ANTHROPIC_API_URL', 'https://relay.stagwellmarketingcloud.io/anthropic/v1/messages')
 
 
 
14
  API_KEY = os.getenv('ANTHROPIC_API_KEY')
15
 
16
  # Configure logging
17
  logging.basicConfig(level=logging.INFO)
18
  logger = logging.getLogger(__name__)
19
 
20
- def handle_completion_request(request_data):
21
- headers = {
22
- 'Authorization': f'Bearer {API_KEY}',
23
- 'Content-Type': 'application/json'
24
- }
25
-
26
- try:
27
- # Transform to Anthropic format
28
- anthropic_request = {
29
- "model": "claude-3-sonnet-20240229",
30
  "max_tokens": request_data.get('max_tokens', 1024),
31
- "messages": [{"role": "user", "content": request_data.get('prompt', '')}]
32
  }
33
-
34
- # Make request to Anthropic
35
- response = requests.post(
36
- ANTHROPIC_API_URL,
37
- headers=headers,
38
- json=anthropic_request
39
- )
40
- response.raise_for_status()
41
-
42
- # Get Anthropic response
43
- anthropic_response = response.json()
44
-
45
- # Transform to OpenAI format
46
- openai_response = {
47
- "id": "cmpl-" + anthropic_response.get('id', 'default'),
48
- "object": "text_completion",
49
- "created": anthropic_response.get('created', 0),
50
- "choices": [{
51
- "text": anthropic_response['content'][0]['text'],
52
- "index": 0,
53
- "finish_reason": "stop"
54
- }],
55
- "usage": {
56
- "prompt_tokens": -1,
57
- "completion_tokens": -1,
58
- "total_tokens": -1
59
- }
60
  }
61
-
62
- return jsonify(openai_response), 200
63
-
64
- except requests.RequestException as e:
65
- logger.error(f"Error communicating with Anthropic API: {str(e)}")
66
- if e.response:
67
- logger.error(f"Response content: {e.response.text}")
68
- return jsonify({"error": "Error communicating with AI service"}), 500
69
- except Exception as e:
70
- logger.error(f"Unexpected error: {str(e)}")
71
- return jsonify({"error": "An unexpected error occurred"}), 500
72
 
73
- def stream_completion(request_data, is_chat=False):
 
 
 
74
  headers = {
75
  'Authorization': f'Bearer {API_KEY}',
76
  'Content-Type': 'application/json',
77
- 'Accept': 'text/event-stream'
78
  }
79
 
80
  try:
81
- # Transform to Anthropic format
82
- anthropic_request = {
83
- "model": "claude-3-sonnet-20240229",
84
- "max_tokens": request_data.get('max_tokens', 1024),
85
- "stream": True
86
- }
87
-
88
- if is_chat:
89
- anthropic_request["messages"] = request_data.get('messages', [])
90
- else:
91
- anthropic_request["messages"] = [{"role": "user", "content": request_data.get('prompt', '')}]
92
-
93
- logger.info(f"Sending streaming request to Anthropic: {json.dumps(anthropic_request)}")
94
 
95
- # Make streaming request to Anthropic
96
  response = requests.post(
97
  ANTHROPIC_API_URL,
98
  headers=headers,
99
  json=anthropic_request,
100
- stream=True
101
  )
102
  response.raise_for_status()
103
 
104
- def generate():
105
- for line in response.iter_lines():
106
- if line:
107
- line_text = line.decode('utf-8')
108
- logger.info(f"Received line: {line_text}")
109
-
110
- # Skip empty lines
111
- if not line_text.strip():
112
- continue
113
-
114
- # Handle SSE prefix
115
- if line_text.startswith('data: '):
116
- line_text = line_text[6:] # Remove 'data: ' prefix
117
-
118
- try:
119
- # Skip [DONE] message
120
- if line_text.strip() == '[DONE]':
121
- yield "data: [DONE]\n\n"
122
- continue
123
-
124
- data = json.loads(line_text)
125
- logger.info(f"Parsed data: {json.dumps(data)}")
126
-
127
- if is_chat:
128
- chunk = {
129
- "id": "chatcmpl-" + data.get('id', 'default'),
130
- "object": "chat.completion.chunk",
131
- "created": data.get('created', 0),
132
- "model": "claude-3-sonnet-20240229",
133
- "choices": [{
134
- "index": 0,
135
- "delta": {
136
- "role": "assistant",
137
- "content": data.get('content', [{}])[0].get('text', '')
138
- },
139
- "finish_reason": data.get('stop_reason')
140
- }]
141
- }
142
- else:
143
- chunk = {
144
- "id": "cmpl-" + data.get('id', 'default'),
145
- "object": "text_completion",
146
- "created": data.get('created', 0),
147
- "choices": [{
148
- "text": data.get('content', [{}])[0].get('text', ''),
149
- "index": 0,
150
- "finish_reason": data.get('stop_reason')
151
- }]
152
- }
153
-
154
- yield f"data: {json.dumps(chunk)}\n\n"
155
-
156
- if data.get('stop_reason'):
157
- yield "data: [DONE]\n\n"
158
-
159
- except json.JSONDecodeError as e:
160
- logger.error(f"Error decoding JSON: {e}")
161
- logger.error(f"Problematic line: {line_text}")
162
- continue
163
- except Exception as e:
164
- logger.error(f"Error processing stream: {str(e)}")
165
- continue
166
-
167
- return Response(
168
- stream_with_context(generate()),
169
- mimetype='text/event-stream',
170
- headers={
171
- 'Cache-Control': 'no-cache',
172
- 'Transfer-Encoding': 'chunked'
173
- }
174
- )
175
 
176
  except requests.RequestException as e:
177
- logger.error(f"Error communicating with Anthropic API: {str(e)}")
178
- if e.response:
179
- logger.error(f"Response content: {e.response.text}")
180
- return jsonify({"error": "Error communicating with AI service"}), 500
 
181
  except Exception as e:
182
- logger.error(f"Unexpected error: {str(e)}")
183
- return jsonify({"error": "An unexpected error occurred"}), 500
 
184
 
 
185
  @app.route('/v1/completions', methods=['POST'])
186
  def completions():
187
- request_data = request.json
188
- if request_data.get('stream', False):
189
- return stream_completion(request_data, is_chat=False)
190
- return handle_completion_request(request_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  @app.route('/v1/chat/completions', methods=['POST'])
193
  def chat_completions():
194
- request_data = request.json
195
- if request_data.get('stream', False):
196
- return stream_completion(request_data, is_chat=True)
197
-
198
- headers = {
199
- 'Authorization': f'Bearer {API_KEY}',
200
- 'Content-Type': 'application/json'
201
- }
202
 
203
- try:
204
- anthropic_request = {
205
- "model": "claude-3-sonnet-20240229",
206
- "max_tokens": request_data.get('max_tokens', 1024),
207
- "messages": request_data.get('messages', [])
208
- }
209
-
210
- response = requests.post(
211
- ANTHROPIC_API_URL,
212
- headers=headers,
213
- json=anthropic_request
214
- )
215
- response.raise_for_status()
216
 
217
- anthropic_response = response.json()
218
-
219
- openai_response = {
220
- "id": "chatcmpl-" + anthropic_response.get('id', 'default'),
221
- "object": "chat.completion",
222
- "created": anthropic_response.get('created', 0),
223
- "choices": [{
224
- "index": 0,
225
- "message": {
226
- "role": "assistant",
227
- "content": anthropic_response['content'][0]['text']
228
- },
229
- "finish_reason": "stop"
230
- }],
231
- "usage": {
232
- "prompt_tokens": -1,
233
- "completion_tokens": -1,
234
- "total_tokens": -1
235
- }
236
  }
237
-
238
- return jsonify(openai_response), 200
239
-
240
- except requests.RequestException as e:
241
- logger.error(f"Error communicating with Anthropic API: {str(e)}")
242
- if e.response:
243
- logger.error(f"Response content: {e.response.text}")
244
- return jsonify({"error": "Error communicating with AI service"}), 500
245
- except Exception as e:
246
- logger.error(f"Unexpected error: {str(e)}")
247
- return jsonify({"error": "An unexpected error occurred"}), 500
248
 
249
  if __name__ == '__main__':
250
  app.run(host='0.0.0.0', port=4224)
 
1
  import os
2
+ from flask import Flask, request, jsonify
3
  import requests
4
  import logging
5
  from dotenv import load_dotenv
6
  import json
7
+ from typing import Dict, Any, Tuple, Union
8
 
9
  # Load environment variables
10
  load_dotenv()
11
 
12
  app = Flask(__name__)
13
 
14
+ ANTHROPIC_API_URL = os.getenv(
15
+ 'ANTHROPIC_API_URL',
16
+ 'https://relay.stagwellmarketingcloud.io/anthropic/v1/messages'
17
+ )
18
  API_KEY = os.getenv('ANTHROPIC_API_KEY')
19
 
20
  # Configure logging
21
  logging.basicConfig(level=logging.INFO)
22
  logger = logging.getLogger(__name__)
23
 
24
+ def transform_to_anthropic_request(
25
+ request_data: Dict[str, Any]
26
+ ) -> Dict[str, Any]:
27
+ """Transform OpenAI-style request to Anthropic format."""
28
+ if 'messages' in request_data:
29
+ # Chat completion format
30
+ return {
31
+ "model": request_data.get('model', 'claude-3-5-sonnet-20240620'),
 
 
32
  "max_tokens": request_data.get('max_tokens', 1024),
33
+ "messages": request_data['messages']
34
  }
35
+ else:
36
+ # Regular completion format
37
+ return {
38
+ "model": request_data.get('model', 'claude-3-5-sonnet-20240620'),
39
+ "max_tokens": request_data.get('max_tokens', 1024),
40
+ "messages": [{
41
+ "role": "user",
42
+ "content": request_data.get('prompt', '')
43
+ }]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ def make_anthropic_request(
47
+ request_data: Dict[str, Any]
48
+ ) -> Tuple[Dict[str, Any], int]:
49
+ """Make request to Anthropic API with proper error handling."""
50
  headers = {
51
  'Authorization': f'Bearer {API_KEY}',
52
  'Content-Type': 'application/json',
 
53
  }
54
 
55
  try:
56
+ anthropic_request = transform_to_anthropic_request(request_data)
57
+ logger.info(
58
+ f"Sending request to Anthropic API: {json.dumps(anthropic_request)}"
59
+ )
 
 
 
 
 
 
 
 
 
60
 
 
61
  response = requests.post(
62
  ANTHROPIC_API_URL,
63
  headers=headers,
64
  json=anthropic_request,
65
+ timeout=30
66
  )
67
  response.raise_for_status()
68
 
69
+ return response.json(), 200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  except requests.RequestException as e:
72
+ error_msg = f"Error communicating with Anthropic API: {str(e)}"
73
+ if hasattr(e, 'response') and e.response is not None:
74
+ error_msg += f"\nResponse: {e.response.text}"
75
+ logger.error(error_msg)
76
+ return {"error": error_msg}, e.response.status_code if e.response else 500
77
  except Exception as e:
78
+ error_msg = f"Unexpected error: {str(e)}"
79
+ logger.error(error_msg)
80
+ return {"error": error_msg}, 500
81
 
82
+ # Routes remain mostly unchanged, but use new helper functions
83
  @app.route('/v1/completions', methods=['POST'])
84
  def completions():
85
+ """Handle completion-style requests."""
86
+ logger.info(f"Received completion request: {json.dumps(request.json)}")
87
+
88
+ anthropic_response, status_code = make_anthropic_request(request.json)
89
+ if status_code != 200:
90
+ return jsonify(anthropic_response), status_code
91
+
92
+ return jsonify({
93
+ "id": "cmpl-" + anthropic_response.get('id', 'default'),
94
+ "object": "text_completion",
95
+ "created": anthropic_response.get('created', 0),
96
+ "choices": [{
97
+ "text": anthropic_response['content'][0]['text'],
98
+ "index": 0,
99
+ "finish_reason": "stop"
100
+ }],
101
+ "usage": {
102
+ "prompt_tokens": -1,
103
+ "completion_tokens": -1,
104
+ "total_tokens": -1
105
+ }
106
+ }), 200
107
 
108
  @app.route('/v1/chat/completions', methods=['POST'])
109
  def chat_completions():
110
+ """Handle chat completion-style requests."""
111
+ logger.info(f"Received chat completion request: {json.dumps(request.json)}")
 
 
 
 
 
 
112
 
113
+ anthropic_response, status_code = make_anthropic_request(request.json)
114
+ if status_code != 200:
115
+ return jsonify(anthropic_response), status_code
 
 
 
 
 
 
 
 
 
 
116
 
117
+ return jsonify({
118
+ "id": "chatcmpl-" + anthropic_response.get('id', 'default'),
119
+ "object": "chat.completion",
120
+ "created": anthropic_response.get('created', 0),
121
+ "choices": [{
122
+ "index": 0,
123
+ "message": {
124
+ "role": "assistant",
125
+ "content": anthropic_response['content'][0]['text']
126
+ },
127
+ "finish_reason": "stop"
128
+ }],
129
+ "usage": {
130
+ "prompt_tokens": -1,
131
+ "completion_tokens": -1,
132
+ "total_tokens": -1
 
 
 
133
  }
134
+ }), 200
 
 
 
 
 
 
 
 
 
 
135
 
136
  if __name__ == '__main__':
137
  app.run(host='0.0.0.0', port=4224)