Skip to content
This repository has been archived by the owner on Jan 5, 2025. It is now read-only.

Commit

Permalink
Merge pull request #145 from openchatai/feat/chat_conversation
Browse files Browse the repository at this point in the history
Feat/chat conversation
  • Loading branch information
codebanesr authored Oct 11, 2023
2 parents ca78712 + 9ff7be7 commit a0b95e1
Show file tree
Hide file tree
Showing 15 changed files with 403 additions and 64 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ git clone [email protected]:openchatai/OpenCopilot.git

```
OPENAI_API_KEY=YOUR_TOKEN_HERE
MYSQL_URI=mysql+pymysql://dbuser:dbpass@mysql:3306/opencopilot
```

- gpt-4: Ideal for more complex tasks, but may have slower processing times.
Expand Down
6 changes: 4 additions & 2 deletions backend/app/Http/Api/Controllers/MessageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public function initChat(Request $request): JsonResponse
"logo" => "logo",
"faq" => $faq ?? [],
"inital_questions" => $initialQuestions ?? [],

]
);
}
Expand All @@ -44,13 +43,16 @@ public function sendChat(Request $request)
{
$this->validate($request, [
'content' => 'required|string|max:255',
'session_id' => 'sometimes|string',
'headers' => 'sometimes|array',
]);

$botToken = $request->header('X-Bot-Token');
/** @var Chatbot $bot */
$bot = Chatbot::where('token', $botToken)->first();

$sessionId = $request->input('session_id', md5(rand())); // @todo get it from the dashboard

if (!$bot) {
return response()->json(
[
Expand All @@ -67,7 +69,7 @@ public function sendChat(Request $request)
try {
$client = new Client();
$response = $client->post('http://llm-server:8002/handle', [
'json' => ['text' => $message, 'swagger_url' => $bot->getSwaggerUrl(), 'headers' => $request->input('headers'), 'base_prompt' => $bot->getPromptMessage()],
'json' => ['session_id' =>$sessionId, 'text' => $message, 'swagger_url' => $bot->getSwaggerUrl(), 'headers' => $request->input('headers'), 'base_prompt' => $bot->getPromptMessage()],
]);

// Retrieve the response from the Flask endpoint
Expand Down
122 changes: 82 additions & 40 deletions backend/public/pilot.js

Large diffs are not rendered by default.

31 changes: 26 additions & 5 deletions copilot-widget/lib/contexts/axiosInstance.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import axios, { AxiosInstance } from "axios";
import { ReactNode, createContext, useContext } from "react";
import { ReactNode, createContext, useContext, useMemo } from "react";
import { useConfigData } from "./ConfigData";
import { useSessionId } from "@lib/hooks/useSessionId";

interface AxiosInstanceProps {
axiosInstance: AxiosInstance;
}

function createAxiosInstance(apiUrl?: string, sessionId?: string) {
const instance = axios.create({
baseURL: apiUrl,
headers: {
"X-Session-Id": sessionId,
},
});

instance.interceptors.request.use((config) => {
config.data = {
...config.data,
session_id: sessionId,
};
return config;
});
return instance;
}

const AxiosContext = createContext<AxiosInstanceProps | undefined>(undefined);
// prefred it separated for the future.
export function AxiosProvider({ children }: { children: ReactNode }) {
const config = useConfigData();

const axiosInstance: AxiosInstance = axios.create({
baseURL: config?.apiUrl,
});
const { sessionId } = useSessionId();
const axiosInstance: AxiosInstance = useMemo(
() => createAxiosInstance(config?.apiUrl, sessionId),
[config, sessionId]
);

if (config?.token) {
axiosInstance.defaults.headers["X-Bot-Token"] = config?.token;
Expand Down
18 changes: 18 additions & 0 deletions copilot-widget/lib/hooks/useSessionId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useEffect, useState } from "react";

const SESSION_ID_KEY = "@openchatai:session_id";

export function useSessionId() {
const [sessionId, setSessionId] = useState<string | undefined>(undefined);
useEffect(() => {
const sessionId = localStorage.getItem(SESSION_ID_KEY);
if (sessionId) {
setSessionId(sessionId);
} else {
const newSessionId = Math.random().toString(36).substring(2, 15);
localStorage.setItem(SESSION_ID_KEY, newSessionId);
setSessionId(newSessionId);
}
}, []);
return { sessionId, setSessionId };
}
10 changes: 10 additions & 0 deletions llm-server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from routes.workflow.workflow_controller import workflow
from routes._swagger.controller import _swagger
from typing import Any, Tuple
from utils.config import Config
from utils.__sql import sql_db

logging.basicConfig(level=logging.INFO)

Expand All @@ -12,6 +14,9 @@

app.register_blueprint(workflow, url_prefix="/workflow")
app.register_blueprint(_swagger, url_prefix="/swagger_api")


app.config.from_object(Config)
from routes.root_service import handle_request


Expand All @@ -33,5 +38,10 @@ def internal_server_error(error: Any) -> Tuple[str, int]:
return "Internal Server Error", 500


sql_db.init_app(app)

with app.app_context():
sql_db.create_all()

if __name__ == "__main__":
app.run(host="0.0.0.0", port=8002, debug=True)
18 changes: 18 additions & 0 deletions llm-server/models/chat_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from utils.__sql import sql_db
from sqlalchemy import Column, String, DateTime, Boolean, Integer
import datetime
from uuid import uuid4


class ChatHistory(sql_db.Model):
__tablename__ = "chat_history"

id = Column(Integer, primary_key=True, autoincrement=True)
chatbot_id = Column(String(36), nullable=True)
session_id = Column(String(255), nullable=True)
from_user = Column(Boolean, default=False)
message = Column(String(8192))
created_at = Column(DateTime, default=datetime.datetime.utcnow)
updated_at = Column(
DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow
)
122 changes: 122 additions & 0 deletions llm-server/models/repository/chat_history_repo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from datetime import datetime
from typing import Optional, cast, List
from utils.__sql import sql_db
from models.chat_history import ChatHistory


def create_chat_history(
chatbot_id: str,
session_id: str,
from_user: str,
message: str,
) -> ChatHistory:
"""Creates a new chat history record.
Args:
chatbot_id: The ID of the chatbot that sent the message.
session_id: The ID of the chat session.
from_user: The user who sent the message.
message: The message content.
Returns:
The newly created ChatHistory object.
"""

chat_history = ChatHistory(
chatbot_id=chatbot_id,
session_id=session_id,
from_user=from_user,
message=message,
)
sql_db.session.add(chat_history)
sql_db.session.commit()
return chat_history


from datetime import datetime
from typing import Optional


def get_all_chat_history_by_session_id(
session_id: str, limit: int = 20, offset: int = 0
) -> List[ChatHistory]:
"""Retrieves all chat history records for a given session ID, sorted by created_at in descending order (most recent first).
Args:
session_id: The ID of the session to retrieve chat history for.
limit: The maximum number of chat history records to retrieve.
offset: The offset at which to start retrieving chat history records.
Returns:
A list of ChatHistory objects, sorted by created_at in descending order.
"""

chats = (
ChatHistory.query.filter_by(session_id=session_id)
.order_by(ChatHistory.created_at.desc())
.limit(limit)
.offset(offset)
.all()
)

# Sort the chat history records by created_at in descending order.
chats.sort(key=lambda chat: chat.created_at)

return cast(List[ChatHistory], chats)


def get_all_chat_history(limit: int = 10, offset: int = 0) -> List[ChatHistory]:
"""Retrieves all chat history records.
Args:
limit: The maximum number of chat history records to retrieve.
offset: The offset at which to start retrieving chat history records.
Returns:
A list of ChatHistory objects.
"""

chats = ChatHistory.query.limit(limit).offset(offset).all()
return cast(List[ChatHistory], chats)


def update_chat_history(
chat_history_id: str,
chatbot_id: Optional[str] = None,
session_id: Optional[str] = None,
from_user: Optional[str] = None,
message: Optional[str] = None,
) -> ChatHistory:
"""Updates a chat history record.
Args:
chat_history_id: The ID of the chat history record to update.
chatbot_id: The new chatbot ID.
session_id: The new session ID.
from_user: The new user name.
message: The new message content.
Returns:
The updated ChatHistory object.
"""

chat_history = ChatHistory.query.get(chat_history_id)
chat_history.chatbot_id = chatbot_id or chat_history.chatbot_id
chat_history.session_id = session_id or chat_history.session_id
chat_history.from_user = from_user or chat_history.from_user
chat_history.message = message or chat_history.message
chat_history.updated_at = datetime.now()
sql_db.session.commit()
return cast(ChatHistory, chat_history)


def delete_chat_history(chat_history_id: str) -> None:
"""Deletes a chat history record.
Args:
chat_history_id: The ID of the chat history record to delete.
"""

chat_history = ChatHistory.query.get(chat_history_id)
sql_db.session.delete(chat_history)
sql_db.session.commit()
11 changes: 7 additions & 4 deletions llm-server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Faker==19.5.0
filelock==3.12.2
Flask==2.3.2
Flask-PyMongo==2.3.0
Flask-SQLAlchemy==3.1.1
frozenlist==1.4.0
fsspec==2023.6.0
google-search-results==2.4.2
Expand Down Expand Up @@ -56,6 +57,7 @@ langsmith==0.0.40
lazy-object-proxy==1.9.0
litellm==0.1.729
loguru==0.7.1
Mako==1.2.4
manifest-ml==0.0.1
MarkupSafe==2.1.3
marshmallow==3.19.0
Expand Down Expand Up @@ -83,13 +85,14 @@ preshed==3.0.8
protobuf==4.24.2
pydantic==1.10.11
pymongo==4.5.0
PyMySQL==1.1.0
python-dateutil==2.8.2
python-dotenv==1.0.0
PyYAML==6.0
qdrant-client==1.4.0
redis==4.6.0
referencing==0.30.2
regex==2023.8.8
regex==2023.6.3
requests==2.31.0
rfc3339-validator==0.1.4
rpds-py==0.10.2
Expand All @@ -102,9 +105,9 @@ sniffio==1.3.0
soupsieve==2.4.1
spacy-legacy==3.0.12
spacy-loggers==1.0.4
SQLAlchemy==1.4.49
SQLAlchemy==2.0.21
sqlitedict==2.1.0
srsly==2.4.8
srsly==2.4.6
tenacity==8.2.2
thinc==8.1.10
tiktoken==0.4.0
Expand All @@ -116,7 +119,7 @@ types-PyYAML==6.0.12.11
types-requests==2.31.0.2
types-urllib3==1.26.25.14
typing-inspect==0.9.0
urllib3==1.26.17
urllib3==1.26.16
wasabi==1.1.2
Werkzeug==2.3.6
wikipedia==1.4.0
Expand Down
21 changes: 18 additions & 3 deletions llm-server/routes/root_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from utils.db import Database
import json
from api_caller.base import try_to_match_and_call_api_endpoint
from models.repository.chat_history_repo import create_chat_history

db_instance = Database()
mongo = db_instance.get_db()
Expand All @@ -45,12 +46,12 @@
def handle_request(data: Dict[str, Any]) -> Any:
text: str = cast(str, data.get("text"))
swagger_url = cast(str, data.get("swagger_url", ""))
session_id = cast(str, data.get("session_id", ""))
base_prompt = data.get("base_prompt", "")
headers = data.get("headers", {})
server_base_url = cast(str, data.get("server_base_url", ""))

logging.info("[OpenCopilot] Got the following user request: {}".format(text))

for required_field, error_msg in [
("base_prompt", BASE_PROMPT_REQUIRED),
("text", TEXT_REQUIRED),
Expand All @@ -68,7 +69,7 @@ def handle_request(data: Dict[str, Any]) -> Any:
"[OpenCopilot] Trying to figure out if the user request require 1) APIs calls 2) If yes how many "
"of them"
)
bot_response = hasSingleIntent(swagger_doc, text)
bot_response = hasSingleIntent(swagger_doc, text, session_id)
if len(bot_response.ids) >= 1:
logging.info(
"[OpenCopilot] Apparently, the user request require calling more than single API endpoint "
Expand All @@ -88,14 +89,28 @@ def handle_request(data: Dict[str, Any]) -> Any:
_workflow = create_workflow_from_operation_ids(
bot_response.ids, SWAGGER_SPEC=swagger_doc
)
return run_workflow(
output = run_workflow(
_workflow,
swagger_doc,
WorkflowData(text, headers, server_base_url, swagger_url),
)

create_chat_history(swagger_url, session_id, True, text)
# bot response
create_chat_history(
swagger_url, session_id, False, output["response"] or output["error"]
)

return output

elif len(bot_response.ids) == 0:
logging.info("[OpenCopilot] The user request doesnot require an api call")

create_chat_history(swagger_url, session_id, True, text)
# bot response
create_chat_history(
swagger_url, session_id, False, bot_response.bot_message
)
return {"response": bot_response.bot_message}

else:
Expand Down
Loading

0 comments on commit a0b95e1

Please sign in to comment.