bahi-bh commited on
Commit
d3e94f4
ยท
verified ยท
1 Parent(s): 53c3e06

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +248 -158
app.py CHANGED
@@ -2,34 +2,39 @@ from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from fastapi.responses import StreamingResponse, JSONResponse
4
  from pydantic import BaseModel
5
- from typing import List
6
  import asyncio
7
  import json
8
  import time
9
  import uuid
10
-
11
  import g4f
12
- import g4f.models
 
 
 
13
 
14
  # =====================================================
15
- # CONFIG
16
  # =====================================================
 
 
17
 
18
- API_KEY = "sk-your-secret-key"
 
 
 
19
 
20
  # =====================================================
21
  # FASTAPI
22
  # =====================================================
23
-
24
  app = FastAPI(
25
  title="Universal AI Gateway",
26
- version="3.0.0"
27
  )
28
 
29
  # =====================================================
30
- # CORS
31
  # =====================================================
32
-
33
  app.add_middleware(
34
  CORSMiddleware,
35
  allow_origins=["*"],
@@ -41,7 +46,6 @@ app.add_middleware(
41
  # =====================================================
42
  # MODELS
43
  # =====================================================
44
-
45
  class Message(BaseModel):
46
  role: str
47
  content: str
@@ -50,31 +54,25 @@ class ChatRequest(BaseModel):
50
  model: str
51
  messages: List[Message]
52
  stream: bool = False
53
- temperature: float = 0.7
54
- max_tokens: int = 4096
55
 
56
  # =====================================================
57
  # AUTH
58
  # =====================================================
59
-
60
  def verify_api_key(req: Request):
61
-
62
  auth = req.headers.get("Authorization")
63
-
64
  if not auth:
65
  raise HTTPException(
66
  status_code=401,
67
  detail="Missing Authorization Header"
68
  )
69
-
70
  if not auth.startswith("Bearer "):
71
  raise HTTPException(
72
  status_code=401,
73
  detail="Invalid Authorization Format"
74
  )
75
-
76
  token = auth.replace("Bearer ", "")
77
-
78
  if token != API_KEY:
79
  raise HTTPException(
80
  status_code=403,
@@ -82,186 +80,191 @@ def verify_api_key(req: Request):
82
  )
83
 
84
  # =====================================================
85
- # PROVIDERS
86
  # =====================================================
87
-
88
- def get_providers():
89
-
90
  providers = []
91
-
92
- if hasattr(g4f.Provider, "DuckDuckGo"):
93
- providers.append(g4f.Provider.DuckDuckGo)
94
-
95
- if hasattr(g4f.Provider, "Blackbox"):
96
- providers.append(g4f.Provider.Blackbox)
97
-
98
- if hasattr(g4f.Provider, "Free2GPT"):
99
- providers.append(g4f.Provider.Free2GPT)
100
-
101
- return providers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
  # =====================================================
104
  # ROOT
105
  # =====================================================
106
-
107
  @app.get("/")
108
  async def root():
109
-
110
  return {
111
  "status": "online",
112
  "service": "Universal AI Gateway",
113
- "version": "3.0.0"
114
  }
115
 
116
  # =====================================================
117
- # MODELS
118
  # =====================================================
119
-
120
  @app.get("/v1/models")
121
- async def models():
122
-
123
- data = []
124
-
125
  try:
126
-
127
- all_models = list(g4f.models._all_models)
128
-
129
- for model_name in all_models:
130
-
131
- data.append({
132
- "id": str(model_name),
133
- "object": "model",
134
- "created": int(time.time()),
135
- "owned_by": "g4f"
136
- })
137
-
138
- except Exception:
139
-
140
- fallback = [
141
- "gpt-4o-mini",
142
- "gpt-4",
143
- "claude-3-haiku",
144
- "llama-3.1-70b",
145
- "mixtral-8x7b"
146
  ]
147
-
148
- for model_name in fallback:
149
-
150
- data.append({
151
  "id": model_name,
152
  "object": "model",
153
  "created": int(time.time()),
154
  "owned_by": "g4f"
155
  })
156
-
157
  return {
158
  "object": "list",
159
- "data": data
160
  }
161
 
162
  # =====================================================
163
- # CHAT COMPLETIONS
164
  # =====================================================
165
-
166
  @app.post("/v1/chat/completions")
167
  async def chat_completions(
168
  req: Request,
169
  body: ChatRequest
170
  ):
171
-
172
  verify_api_key(req)
173
-
 
174
  messages = [
175
- {
176
- "role": m.role,
177
- "content": m.content
178
- }
179
  for m in body.messages
180
  ]
181
-
 
 
182
  # =================================================
183
- # STREAMING
184
  # =================================================
185
-
186
  if body.stream:
187
-
188
  async def generate_stream():
189
-
190
- providers = get_providers()
191
-
192
  last_error = None
193
-
194
- for provider in providers:
195
-
 
196
  try:
197
-
198
- response = await asyncio.to_thread(
199
- g4f.ChatCompletion.create,
 
 
200
  model=body.model,
201
- provider=provider,
202
  messages=messages,
203
  stream=True
204
  )
205
-
206
- for chunk in response:
207
-
208
- if chunk:
209
-
210
  payload = {
211
- "id": f"chatcmpl-{uuid.uuid4().hex}",
212
  "object": "chat.completion.chunk",
213
  "created": int(time.time()),
214
  "model": body.model,
215
- "choices": [
216
- {
217
- "index": 0,
218
- "delta": {
219
- "content": str(chunk)
220
- },
221
- "finish_reason": None
222
- }
223
- ]
224
  }
225
-
226
  yield f"data: {json.dumps(payload)}\n\n"
227
-
228
- await asyncio.sleep(0)
229
-
230
- done_payload = {
231
- "id": f"chatcmpl-{uuid.uuid4().hex}",
232
  "object": "chat.completion.chunk",
233
  "created": int(time.time()),
234
  "model": body.model,
235
- "choices": [
236
- {
237
- "index": 0,
238
- "delta": {},
239
- "finish_reason": "stop"
240
- }
241
- ]
242
  }
243
-
244
- yield f"data: {json.dumps(done_payload)}\n\n"
245
-
246
  yield "data: [DONE]\n\n"
247
-
248
  return
249
-
250
  except Exception as e:
251
-
252
  last_error = str(e)
253
-
254
  continue
255
-
 
256
  error_payload = {
257
  "error": {
258
  "message": last_error or "All providers failed",
259
  "type": "server_error"
260
  }
261
  }
262
-
263
  yield f"data: {json.dumps(error_payload)}\n\n"
264
-
265
  return StreamingResponse(
266
  generate_stream(),
267
  media_type="text/event-stream",
@@ -271,67 +274,154 @@ async def chat_completions(
271
  "X-Accel-Buffering": "no"
272
  }
273
  )
274
-
275
  # =================================================
276
- # NORMAL RESPONSE
277
  # =================================================
278
-
279
- providers = get_providers()
280
-
281
  last_error = None
282
-
283
- for provider in providers:
284
-
285
  try:
286
-
 
 
 
287
  response = await asyncio.to_thread(
288
- g4f.ChatCompletion.create,
289
  model=body.model,
290
- provider=provider,
291
  messages=messages
292
  )
293
-
 
 
294
  return JSONResponse({
295
  "id": f"chatcmpl-{uuid.uuid4().hex}",
296
  "object": "chat.completion",
297
  "created": int(time.time()),
298
  "model": body.model,
299
- "choices": [
300
- {
301
- "index": 0,
302
- "message": {
303
- "role": "assistant",
304
- "content": str(response)
305
- },
306
- "finish_reason": "stop"
307
- }
308
- ],
309
  "usage": {
310
  "prompt_tokens": 0,
311
  "completion_tokens": 0,
312
  "total_tokens": 0
313
  }
314
  })
315
-
316
  except Exception as e:
317
-
318
  last_error = str(e)
319
-
320
  continue
321
-
 
322
  raise HTTPException(
323
  status_code=500,
324
- detail=last_error or "All providers failed"
325
  )
326
 
327
  # =====================================================
328
- # RUN
329
  # =====================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
 
 
 
331
  if __name__ == "__main__":
332
-
333
  import uvicorn
334
-
 
 
 
 
 
 
 
 
335
  uvicorn.run(
336
  app,
337
  host="0.0.0.0",
 
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from fastapi.responses import StreamingResponse, JSONResponse
4
  from pydantic import BaseModel
5
+ from typing import List, Optional
6
  import asyncio
7
  import json
8
  import time
9
  import uuid
 
10
  import g4f
11
+ from g4f.client import Client
12
+ from g4f.models import Model
13
+ from g4f.providers import Provider, ProviderType
14
+ import logging
15
 
16
  # =====================================================
17
+ # LOGGING
18
  # =====================================================
19
+ logging.basicConfig(level=logging.INFO)
20
+ logger = logging.getLogger(__name__)
21
 
22
+ # =====================================================
23
+ # CONFIG
24
+ # =====================================================
25
+ API_KEY = "sk-your-secret-key" # ุบูŠุฑ ู‡ุฐุง ููŠ ุงู„ุฅู†ุชุงุฌ
26
 
27
  # =====================================================
28
  # FASTAPI
29
  # =====================================================
 
30
  app = FastAPI(
31
  title="Universal AI Gateway",
32
+ version="3.1.0"
33
  )
34
 
35
  # =====================================================
36
+ # CORS (ู„ู„ุณู…ุงุญ ู„ู„ูˆุงุฌู‡ุฉ ุจุงู„ุชูˆุงุตู„)
37
  # =====================================================
 
38
  app.add_middleware(
39
  CORSMiddleware,
40
  allow_origins=["*"],
 
46
  # =====================================================
47
  # MODELS
48
  # =====================================================
 
49
  class Message(BaseModel):
50
  role: str
51
  content: str
 
54
  model: str
55
  messages: List[Message]
56
  stream: bool = False
57
+ temperature: Optional[float] = 0.7
58
+ max_tokens: Optional[int] = 4096
59
 
60
  # =====================================================
61
  # AUTH
62
  # =====================================================
 
63
  def verify_api_key(req: Request):
 
64
  auth = req.headers.get("Authorization")
 
65
  if not auth:
66
  raise HTTPException(
67
  status_code=401,
68
  detail="Missing Authorization Header"
69
  )
 
70
  if not auth.startswith("Bearer "):
71
  raise HTTPException(
72
  status_code=401,
73
  detail="Invalid Authorization Format"
74
  )
 
75
  token = auth.replace("Bearer ", "")
 
76
  if token != API_KEY:
77
  raise HTTPException(
78
  status_code=403,
 
80
  )
81
 
82
  # =====================================================
83
+ # PROVIDERS - ู‚ุงุฆู…ุฉ ุงู„ู…ุฒูˆุฏูŠู† ุงู„ุนุงู…ู„ูŠู† ุญุงู„ูŠุงู‹
84
  # =====================================================
85
+ def get_working_providers():
86
+ """ุฅุฑุฌุงุน ู‚ุงุฆู…ุฉ ุงู„ู…ุฒูˆุฏูŠู† ุงู„ู…ุชุงุญูŠู† ููŠ ุงู„ุฅุตุฏุงุฑ ุงู„ุญุงู„ูŠ ู…ู† g4f"""
 
87
  providers = []
88
+
89
+ # ุงู„ู…ุฒูˆุฏูŠู† ุงู„ู…ุนุฑูˆููŠู† ุจุฃู†ู‡ู… ูŠุนู…ู„ูˆู† ุญุงู„ูŠุงู‹
90
+ try:
91
+ # ู…ุญุงูˆู„ุฉ ุงุณุชูŠุฑุงุฏ ุงู„ู…ุฒูˆุฏูŠู† ุงู„ู…ุชุงุญูŠู†
92
+ from g4f.providers import (
93
+ Blackbox,
94
+ DuckDuckGo,
95
+ Free2GPT,
96
+ Liaobots,
97
+ Copilot,
98
+ Gemini,
99
+ OpenaiChat,
100
+ DeepAi
101
+ )
102
+
103
+ # ุฅุถุงูุฉ ุงู„ู…ุฒูˆุฏูŠู† ุงู„ุฐูŠู† ูŠุนู…ู„ูˆู† ุนุงุฏุฉู‹
104
+ providers = [
105
+ DuckDuckGo,
106
+ Blackbox,
107
+ Free2GPT,
108
+ Liaobots,
109
+ Copilot,
110
+ Gemini,
111
+ OpenaiChat,
112
+ DeepAi
113
+ ]
114
+ except ImportError:
115
+ logger.warning("ุจุนุถ ุงู„ู…ุฒูˆุฏูŠู† ุบูŠุฑ ู…ุชุงุญูŠู†ุŒ ุงุณุชุฎุฏุงู… ุงู„ู…ุฒูˆุฏูŠู† ุงู„ุฃุณุงุณูŠูŠู†")
116
+ # ู…ุญุงูˆู„ุฉ ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ู…ุฒูˆุฏูŠู† ู…ู† g4f.Provider ู…ุจุงุดุฑุฉ
117
+ if hasattr(g4f.Provider, 'DuckDuckGo'):
118
+ providers.append(g4f.Provider.DuckDuckGo)
119
+ if hasattr(g4f.Provider, 'Blackbox'):
120
+ providers.append(g4f.Provider.Blackbox)
121
+ if hasattr(g4f.Provider, 'Free2GPT'):
122
+ providers.append(g4f.Provider.Free2GPT)
123
+ if hasattr(g4f.Provider, 'Liaobots'):
124
+ providers.append(g4f.Provider.Liaobots)
125
+
126
+ return providers if providers else [g4f.Provider.Auto]
127
 
128
  # =====================================================
129
  # ROOT
130
  # =====================================================
 
131
  @app.get("/")
132
  async def root():
 
133
  return {
134
  "status": "online",
135
  "service": "Universal AI Gateway",
136
+ "version": "3.1.0"
137
  }
138
 
139
  # =====================================================
140
+ # MODELS LIST (ู†ู‚ุทุฉ ู†ู‡ุงูŠุฉ ู…ุชูˆุงูู‚ุฉ ู…ุน OpenAI)
141
  # =====================================================
 
142
  @app.get("/v1/models")
143
+ async def get_models():
144
+ """ุฅุฑุฌุงุน ู‚ุงุฆู…ุฉ ุงู„ู†ู…ุงุฐุฌ ุงู„ู…ุชุงุญุฉ"""
145
+ models_list = []
146
+
147
  try:
148
+ # ู…ุญุงูˆู„ุฉ ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ู†ู…ุงุฐุฌ ู…ู† g4f
149
+ if hasattr(g4f.models, '_all_models'):
150
+ for model in list(g4f.models._all_models)[:30]: # ุญุฏ ุฃู‚ุตู‰ 30 ู†ู…ูˆุฐุฌ
151
+ models_list.append({
152
+ "id": str(model),
153
+ "object": "model",
154
+ "created": int(time.time()),
155
+ "owned_by": "g4f"
156
+ })
157
+ except Exception as e:
158
+ logger.error(f"ุฎุทุฃ ููŠ ุฌู„ุจ ุงู„ู†ู…ุงุฐุฌ: {e}")
159
+
160
+ # ู†ู…ุงุฐุฌ ุงุญุชูŠุงุทูŠุฉ ุฅุฐุง ูุดู„ ุงู„ุฌู„ุจ
161
+ if not models_list:
162
+ fallback_models = [
163
+ "gpt-4o-mini", "gpt-4o", "gpt-4", "gpt-3.5-turbo",
164
+ "claude-3-haiku", "llama-3.1-70b", "mixtral-8x7b",
165
+ "gemini-flash", "deepseek-chat", "blackboxai"
 
 
166
  ]
167
+ for model_name in fallback_models:
168
+ models_list.append({
 
 
169
  "id": model_name,
170
  "object": "model",
171
  "created": int(time.time()),
172
  "owned_by": "g4f"
173
  })
174
+
175
  return {
176
  "object": "list",
177
+ "data": models_list
178
  }
179
 
180
  # =====================================================
181
+ # CHAT COMPLETIONS - ุงู„ุทุฑูŠู‚ุฉ ุงู„ู…ุญุฏุซุฉ ุจุงุณุชุฎุฏุงู… Client
182
  # =====================================================
 
183
  @app.post("/v1/chat/completions")
184
  async def chat_completions(
185
  req: Request,
186
  body: ChatRequest
187
  ):
 
188
  verify_api_key(req)
189
+
190
+ # ุชุญูˆูŠู„ ุงู„ุฑุณุงุฆู„ ุฅู„ู‰ ุงู„ุดูƒู„ ุงู„ู…ุทู„ูˆุจ
191
  messages = [
192
+ {"role": m.role, "content": m.content}
 
 
 
193
  for m in body.messages
194
  ]
195
+
196
+ logger.info(f"ุทู„ุจ: model={body.model}, messages={len(messages)}, stream={body.stream}")
197
+
198
  # =================================================
199
+ # ูˆุถุน ุงู„ุฏูู‚ (Streaming) - ู„ู„ุชุฌุงูˆุจ ุงู„ููˆุฑูŠ
200
  # =================================================
 
201
  if body.stream:
 
202
  async def generate_stream():
203
+ providers_list = get_working_providers()
 
 
204
  last_error = None
205
+ response_text = ""
206
+ chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
207
+
208
+ for provider in providers_list:
209
  try:
210
+ # ุงุณุชุฎุฏุงู… ุงู„ุทุฑูŠู‚ุฉ ุงู„ุญุฏูŠุซุฉ ู…ุน Client
211
+ client = Client(provider=provider)
212
+
213
+ stream_response = await asyncio.to_thread(
214
+ client.chat.completions.create,
215
  model=body.model,
 
216
  messages=messages,
217
  stream=True
218
  )
219
+
220
+ for chunk in stream_response:
221
+ if chunk.choices and chunk.choices[0].delta.content:
222
+ content = chunk.choices[0].delta.content
223
+ response_text += content
224
  payload = {
225
+ "id": chunk_id,
226
  "object": "chat.completion.chunk",
227
  "created": int(time.time()),
228
  "model": body.model,
229
+ "choices": [{
230
+ "index": 0,
231
+ "delta": {"content": content},
232
+ "finish_reason": None
233
+ }]
 
 
 
 
234
  }
 
235
  yield f"data: {json.dumps(payload)}\n\n"
236
+ await asyncio.sleep(0.01)
237
+
238
+ # ุฅุฑุณุงู„ ุฅุดุงุฑุฉ ุงู„ุงู†ุชู‡ุงุก
239
+ final_payload = {
240
+ "id": chunk_id,
241
  "object": "chat.completion.chunk",
242
  "created": int(time.time()),
243
  "model": body.model,
244
+ "choices": [{
245
+ "index": 0,
246
+ "delta": {},
247
+ "finish_reason": "stop"
248
+ }]
 
 
249
  }
250
+ yield f"data: {json.dumps(final_payload)}\n\n"
 
 
251
  yield "data: [DONE]\n\n"
 
252
  return
253
+
254
  except Exception as e:
 
255
  last_error = str(e)
256
+ logger.warning(f"ุงู„ู…ุฒูˆุฏ {provider.__name__ if hasattr(provider, '__name__') else provider} ูุดู„: {e}")
257
  continue
258
+
259
+ # ุฅุฐุง ูุดู„ุช ูƒู„ ุงู„ู…ุฒูˆุฏูŠู†
260
  error_payload = {
261
  "error": {
262
  "message": last_error or "All providers failed",
263
  "type": "server_error"
264
  }
265
  }
 
266
  yield f"data: {json.dumps(error_payload)}\n\n"
267
+
268
  return StreamingResponse(
269
  generate_stream(),
270
  media_type="text/event-stream",
 
274
  "X-Accel-Buffering": "no"
275
  }
276
  )
277
+
278
  # =================================================
279
+ # ุงู„ูˆุถุน ุงู„ุนุงุฏูŠ (ุจุฏูˆู† ุฏูู‚)
280
  # =================================================
281
+ providers_list = get_working_providers()
 
 
282
  last_error = None
283
+
284
+ for provider in providers_list:
 
285
  try:
286
+ logger.info(f"ู…ุญุงูˆู„ุฉ ุงุณุชุฎุฏุงู… ุงู„ู…ุฒูˆุฏ: {provider.__name__ if hasattr(provider, '__name__') else provider}")
287
+
288
+ # ุงู„ุทุฑูŠู‚ุฉ ุงู„ุญุฏูŠุซุฉ ุจุงุณุชุฎุฏุงู… Client
289
+ client = Client(provider=provider)
290
  response = await asyncio.to_thread(
291
+ client.chat.completions.create,
292
  model=body.model,
 
293
  messages=messages
294
  )
295
+
296
+ assistant_message = response.choices[0].message.content
297
+
298
  return JSONResponse({
299
  "id": f"chatcmpl-{uuid.uuid4().hex}",
300
  "object": "chat.completion",
301
  "created": int(time.time()),
302
  "model": body.model,
303
+ "choices": [{
304
+ "index": 0,
305
+ "message": {
306
+ "role": "assistant",
307
+ "content": assistant_message
308
+ },
309
+ "finish_reason": "stop"
310
+ }],
 
 
311
  "usage": {
312
  "prompt_tokens": 0,
313
  "completion_tokens": 0,
314
  "total_tokens": 0
315
  }
316
  })
317
+
318
  except Exception as e:
 
319
  last_error = str(e)
320
+ logger.warning(f"ุงู„ู…ุฒูˆุฏ ูุดู„: {e}")
321
  continue
322
+
323
+ # ุฅุฐุง ูˆุตู„ู†ุง ุฅู„ู‰ ู‡ู†ุงุŒ ุฌู…ูŠุน ุงู„ู…ุฒูˆุฏูŠู† ูุดู„ูˆุง
324
  raise HTTPException(
325
  status_code=500,
326
+ detail=last_error or "All providers failed. Please check your internet connection and try again."
327
  )
328
 
329
  # =====================================================
330
+ # ู†ู‚ุทุฉ ู†ู‡ุงูŠุฉ ุฅุถุงููŠุฉ ู„ู„ุงุฎุชุจุงุฑ - ูˆุงุฌู‡ุฉ ุจุณูŠุทุฉ
331
  # =====================================================
332
+ @app.get("/test")
333
+ async def test_ui():
334
+ """ุตูุญุฉ HTML ุจุณูŠุทุฉ ู„ุงุฎุชุจุงุฑ ุงู„ุจูˆุช"""
335
+ return """
336
+ <!DOCTYPE html>
337
+ <html>
338
+ <head>
339
+ <title>ุงุฎุชุจุงุฑ ุงู„ุจูˆุช</title>
340
+ <meta charset="utf-8">
341
+ <style>
342
+ body { font-family: Arial; max-width: 800px; margin: 50px auto; padding: 20px; }
343
+ #chat { border: 1px solid #ccc; height: 400px; overflow-y: auto; padding: 10px; margin-bottom: 10px; }
344
+ .user { background: #e3f2fd; padding: 8px; margin: 5px; border-radius: 10px; }
345
+ .bot { background: #f5f5f5; padding: 8px; margin: 5px; border-radius: 10px; }
346
+ #input { width: 80%; padding: 10px; }
347
+ #send { padding: 10px 20px; }
348
+ </style>
349
+ </head>
350
+ <body>
351
+ <h1>ุงุฎุชุจุงุฑ ุงู„ุจูˆุช</h1>
352
+ <div id="chat"></div>
353
+ <input type="text" id="input" placeholder="ุงูƒุชุจ ุฑุณุงู„ุชูƒ...">
354
+ <button id="send">ุฅุฑุณุงู„</button>
355
+
356
+ <script>
357
+ const API_KEY = "sk-your-secret-key";
358
+
359
+ async function sendMessage() {
360
+ const input = document.getElementById('input');
361
+ const message = input.value.trim();
362
+ if (!message) return;
363
+
364
+ // ุนุฑุถ ุฑุณุงู„ุฉ ุงู„ู…ุณุชุฎุฏู…
365
+ const chatDiv = document.getElementById('chat');
366
+ const userMsg = document.createElement('div');
367
+ userMsg.className = 'user';
368
+ userMsg.textContent = 'ุฃู†ุช: ' + message;
369
+ chatDiv.appendChild(userMsg);
370
+
371
+ // ุนุฑุถ ู…ุคุดุฑ "ูŠูƒุชุจ..."
372
+ const botMsg = document.createElement('div');
373
+ botMsg.className = 'bot';
374
+ botMsg.textContent = 'ุงู„ุจูˆุช: ุฌุงุฑูŠ ุงู„ุชููƒูŠุฑ...';
375
+ chatDiv.appendChild(botMsg);
376
+ chatDiv.scrollTop = chatDiv.scrollHeight;
377
+
378
+ input.value = '';
379
+
380
+ try {
381
+ const response = await fetch('/v1/chat/completions', {
382
+ method: 'POST',
383
+ headers: {
384
+ 'Content-Type': 'application/json',
385
+ 'Authorization': 'Bearer ' + API_KEY
386
+ },
387
+ body: JSON.stringify({
388
+ model: 'gpt-4o-mini',
389
+ messages: [{role: 'user', content: message}],
390
+ stream: false
391
+ })
392
+ });
393
+
394
+ const data = await response.json();
395
+ botMsg.textContent = 'ุงู„ุจูˆุช: ' + data.choices[0].message.content;
396
+ } catch (err) {
397
+ botMsg.textContent = 'ุงู„ุจูˆุช: ุญุฏุซ ุฎุทุฃ: ' + err.message;
398
+ }
399
+ chatDiv.scrollTop = chatDiv.scrollHeight;
400
+ }
401
+
402
+ document.getElementById('send').onclick = sendMessage;
403
+ document.getElementById('input').onkeypress = (e) => {
404
+ if (e.key === 'Enter') sendMessage();
405
+ };
406
+ </script>
407
+ </body>
408
+ </html>
409
+ """
410
 
411
+ # =====================================================
412
+ # RUN
413
+ # =====================================================
414
  if __name__ == "__main__":
 
415
  import uvicorn
416
+ print("""
417
+ โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
418
+ โ•‘ AI Gateway - ุชู… ุงู„ุชุดุบูŠู„ ุจู†ุฌุงุญ! ๐Ÿš€ โ•‘
419
+ โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
420
+ โ•‘ ุงู„ูˆุงุฌู‡ุฉ ุงู„ุชูุงุนู„ูŠุฉ: http://localhost:7860/testโ•‘
421
+ โ•‘ API: http://localhost:7860/v1/chat/completionsโ•‘
422
+ โ•‘ ุงู„ู…ูุชุงุญ: sk-your-secret-key โ•‘
423
+ โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
424
+ """)
425
  uvicorn.run(
426
  app,
427
  host="0.0.0.0",