-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi.py
More file actions
162 lines (120 loc) · 4.85 KB
/
api.py
File metadata and controls
162 lines (120 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""
api.py — FastAPI REST backend for AI ChatBot.
Run with:
uvicorn api:app --reload
Endpoints:
POST /chat Send a message and receive a reply.
POST /train Reload the pattern-matching bot from a CSV file.
GET /health Health check.
DELETE /sessions/{id} Reset a conversation session.
Environment variables:
OPENAI_API_KEY Optional – enables the LLM backend.
"""
import os
import uuid
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
# Load .env if available
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
from ai_chatbot import ChatBot
# ---------------------------------------------------------------------------
# App setup
# ---------------------------------------------------------------------------
app = FastAPI(
title="AI ChatBot API",
description=(
"REST API for the AI ChatBot. Supports LLM (OpenAI) and offline "
"pattern-matching backends. Maintains per-session conversation history."
),
version="1.0.0",
)
# Per-session bots (keyed by session_id string)
_sessions: dict[str, ChatBot] = {}
# Dialog file used when creating new sessions (updated by /train)
_default_dialog_file: Optional[str] = None
def clear_all_sessions() -> None:
"""Remove all active sessions and reset the default dialog file. Intended for use in tests."""
global _default_dialog_file
_sessions.clear()
_default_dialog_file = None
def _get_or_create_session(session_id: Optional[str]) -> tuple[str, ChatBot]:
"""Return (session_id, ChatBot) for the given session; create if missing."""
if session_id is None:
# Create a new session
session_id = str(uuid.uuid4())
if session_id not in _sessions:
# When a dialog file has been explicitly configured via /train, create
# the bot in offline mode so the file is always used (no Kaggle fallback).
if _default_dialog_file is not None:
_sessions[session_id] = ChatBot(
dialog_file=_default_dialog_file, force_offline=True
)
else:
_sessions[session_id] = ChatBot()
return session_id, _sessions[session_id]
# ---------------------------------------------------------------------------
# Request / Response schemas
# ---------------------------------------------------------------------------
class ChatRequest(BaseModel):
message: str
session_id: Optional[str] = None
class ChatResponse(BaseModel):
reply: str
session_id: str
mode: str
class TrainRequest(BaseModel):
dialog_file: str
class TrainResponse(BaseModel):
status: str
patterns_loaded: int
# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------
@app.get("/health")
def health_check():
"""Simple health check."""
return {"status": "ok"}
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
"""Send a message and receive a reply.
If *session_id* is omitted, a new session is created and its ID is
returned so the client can continue the conversation.
"""
if not request.message.strip():
raise HTTPException(status_code=400, detail="Message must not be empty.")
session_id, bot = _get_or_create_session(request.session_id)
reply = bot.chat(request.message)
return ChatResponse(reply=reply, session_id=session_id, mode=bot.mode)
@app.delete("/sessions/{session_id}")
def reset_session(session_id: str):
"""Delete a conversation session (clears history)."""
if session_id in _sessions:
del _sessions[session_id]
return {"status": "session deleted", "session_id": session_id}
raise HTTPException(status_code=404, detail="Session not found.")
@app.post("/train", response_model=TrainResponse)
def train(request: TrainRequest):
"""Reload pattern-matching data from a CSV file on the server.
The file must exist on the server filesystem. All existing sessions are
cleared so that subsequent requests create new sessions using the updated
training data.
"""
if not os.path.exists(request.dialog_file):
raise HTTPException(
status_code=404,
detail="Training file not found on server. Please verify the file path.",
)
# Retrain a fresh bot to validate and count the patterns
fresh_bot = ChatBot(dialog_file=request.dialog_file, force_offline=True)
patterns = fresh_bot.pattern_count
# Persist the dialog file so new sessions created after this point use it,
# then clear existing sessions so they pick up the new data on next request.
global _default_dialog_file
_default_dialog_file = request.dialog_file
_sessions.clear()
return TrainResponse(status="retrained", patterns_loaded=patterns)