Gemini commited on
Commit
39dcb26
·
1 Parent(s): d078b16

feat: Add chat interface and versioned API

Browse files
Files changed (5) hide show
  1. Dockerfile +3 -1
  2. chat.html +21 -0
  3. main.py +47 -4
  4. static/script.js +47 -0
  5. static/style.css +68 -0
Dockerfile CHANGED
@@ -16,10 +16,12 @@ RUN apt-get update && apt-get install -y python3 python3-pip curl
16
  WORKDIR /app
17
  COPY ./main.py /app/main.py
18
  COPY ./start.sh /app/start.sh
 
 
19
  RUN chmod +x /app/start.sh
20
 
21
  # 3. Install Python dependencies for the FastAPI gateway
22
- RUN pip3 install fastapi uvicorn requests python-dotenv --break-system-packages
23
 
24
  # Expose the port the FastAPI gateway will listen on
25
  EXPOSE 7860
 
16
  WORKDIR /app
17
  COPY ./main.py /app/main.py
18
  COPY ./start.sh /app/start.sh
19
+ COPY ./chat.html /app/chat.html
20
+ COPY ./static /app/static
21
  RUN chmod +x /app/start.sh
22
 
23
  # 3. Install Python dependencies for the FastAPI gateway
24
+ RUN pip3 install fastapi uvicorn requests --break-system-packages
25
 
26
  # Expose the port the FastAPI gateway will listen on
27
  EXPOSE 7860
chat.html ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Ollama Chat</title>
7
+ <link rel="stylesheet" href="/static/style.css">
8
+ </head>
9
+ <body>
10
+ <div id="chat-container">
11
+ <div id="chat-window">
12
+ <ul id="message-list"></ul>
13
+ </div>
14
+ <div id="input-container">
15
+ <input type="text" id="message-input" placeholder="Type your message...">
16
+ <button id="send-button">Send</button>
17
+ </div>
18
+ </div>
19
+ <script src="/static/script.js"></script>
20
+ </body>
21
+ </html>
main.py CHANGED
@@ -1,7 +1,8 @@
1
  import os
2
  import requests
3
- from fastapi import FastAPI, Request, HTTPException
4
- from fastapi.responses import JSONResponse
 
5
 
6
  # Get the secret API key from the environment variables (set in Space secrets)
7
  # This is the key the user must provide to access the service.
@@ -12,8 +13,14 @@ OLLAMA_API_URL = "http://localhost:11434"
12
 
13
  app = FastAPI()
14
 
15
- @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
16
- async def proxy(request: Request, path: str):
 
 
 
 
 
 
17
  # 1. Check for the API Key
18
  provided_key = request.headers.get("X-API-Key")
19
  if not AUTH_KEY or provided_key != AUTH_KEY:
@@ -42,3 +49,39 @@ async def proxy(request: Request, path: str):
42
  )
43
  except Exception as e:
44
  raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import requests
3
+ from fastapi import FastAPI, Request, HTTPException, APIRouter
4
+ from fastapi.responses import JSONResponse, RedirectResponse, FileResponse
5
+ from fastapi.staticfiles import StaticFiles
6
 
7
  # Get the secret API key from the environment variables (set in Space secrets)
8
  # This is the key the user must provide to access the service.
 
13
 
14
  app = FastAPI()
15
 
16
+ # Mount the static directory to serve CSS and JS files
17
+ app.mount("/static", StaticFiles(directory="static"), name="static")
18
+
19
+ # API v1 Router (secured)
20
+ api_v1 = APIRouter()
21
+
22
+ @api_v1.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
23
+ async def proxy_v1(request: Request, path: str):
24
  # 1. Check for the API Key
25
  provided_key = request.headers.get("X-API-Key")
26
  if not AUTH_KEY or provided_key != AUTH_KEY:
 
49
  )
50
  except Exception as e:
51
  raise HTTPException(status_code=500, detail=str(e))
52
+
53
+ app.include_router(api_v1, prefix="/api/v1")
54
+
55
+ # Chat API endpoint (not secured)
56
+ @app.post("/api/chat")
57
+ async def chat_endpoint(request: Request):
58
+ body = await request.json()
59
+ model = body.get("model", "llama3")
60
+ prompt = body.get("prompt")
61
+
62
+ if not prompt:
63
+ raise HTTPException(status_code=400, detail="Prompt is required")
64
+
65
+ url = f"{OLLAMA_API_URL}/api/generate"
66
+
67
+ try:
68
+ response = requests.post(
69
+ url=url,
70
+ json={"model": model, "prompt": prompt, "stream": False}
71
+ )
72
+
73
+ return JSONResponse(
74
+ status_code=response.status_code,
75
+ content=response.json()
76
+ )
77
+ except Exception as e:
78
+ raise HTTPException(status_code=500, detail=str(e))
79
+
80
+
81
+ @app.get("/")
82
+ async def root():
83
+ return RedirectResponse(url="/chat")
84
+
85
+ @app.get("/chat")
86
+ async def chat_page():
87
+ return FileResponse('chat.html')
static/script.js ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const messageList = document.getElementById('message-list');
2
+ const messageInput = document.getElementById('message-input');
3
+ const sendButton = document.getElementById('send-button');
4
+
5
+ sendButton.addEventListener('click', sendMessage);
6
+ messageInput.addEventListener('keydown', (event) => {
7
+ if (event.key === 'Enter') {
8
+ sendMessage();
9
+ }
10
+ });
11
+
12
+ function sendMessage() {
13
+ const message = messageInput.value.trim();
14
+ if (message === '') {
15
+ return;
16
+ }
17
+
18
+ appendMessage(message, 'user-message');
19
+ messageInput.value = '';
20
+
21
+ fetch('/api/chat', {
22
+ method: 'POST',
23
+ headers: {
24
+ 'Content-Type': 'application/json'
25
+ },
26
+ body: JSON.stringify({
27
+ model: 'llama3', // You can change the model here
28
+ prompt: message
29
+ })
30
+ })
31
+ .then(response => response.json())
32
+ .then(data => {
33
+ appendMessage(data.response, 'bot-message');
34
+ })
35
+ .catch(error => {
36
+ console.error('Error:', error);
37
+ appendMessage('Sorry, something went wrong.', 'bot-message');
38
+ });
39
+ }
40
+
41
+ function appendMessage(message, className) {
42
+ const li = document.createElement('li');
43
+ li.textContent = message;
44
+ li.classList.add(className);
45
+ messageList.appendChild(li);
46
+ messageList.scrollTop = messageList.scrollHeight;
47
+ }
static/style.css ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: sans-serif;
3
+ margin: 0;
4
+ background-color: #f0f0f0;
5
+ }
6
+
7
+ #chat-container {
8
+ display: flex;
9
+ flex-direction: column;
10
+ height: 100vh;
11
+ max-width: 800px;
12
+ margin: 0 auto;
13
+ background-color: white;
14
+ border-left: 1px solid #ccc;
15
+ border-right: 1px solid #ccc;
16
+ }
17
+
18
+ #chat-window {
19
+ flex-grow: 1;
20
+ overflow-y: auto;
21
+ padding: 20px;
22
+ }
23
+
24
+ #message-list {
25
+ list-style: none;
26
+ margin: 0;
27
+ padding: 0;
28
+ }
29
+
30
+ #message-list li {
31
+ margin-bottom: 10px;
32
+ padding: 10px;
33
+ border-radius: 5px;
34
+ }
35
+
36
+ #message-list .user-message {
37
+ background-color: #dcf8c6;
38
+ align-self: flex-end;
39
+ }
40
+
41
+ #message-list .bot-message {
42
+ background-color: #f1f0f0;
43
+ align-self: flex-start;
44
+ }
45
+
46
+ #input-container {
47
+ display: flex;
48
+ padding: 20px;
49
+ border-top: 1px solid #ccc;
50
+ }
51
+
52
+ #message-input {
53
+ flex-grow: 1;
54
+ border: 1px solid #ccc;
55
+ border-radius: 5px;
56
+ padding: 10px;
57
+ font-size: 16px;
58
+ }
59
+
60
+ #send-button {
61
+ background-color: #4CAF50;
62
+ color: white;
63
+ border: none;
64
+ padding: 10px 20px;
65
+ border-radius: 5px;
66
+ margin-left: 10px;
67
+ cursor: pointer;
68
+ }