├── Langchain ├── agent │ ├── __init__.py │ └── bing_search.py ├── api.py ├── chains │ ├── dialogue_answering │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── base.py │ │ └── prompts.py │ ├── modules │ │ ├── embeddings.py │ │ └── vectorstores.py │ └── text_load.py └── cli.py ├── Nextjs-Reactjs-frontend ├── .eslintrc.json ├── .gitignore ├── README.md ├── components │ ├── PromptInput │ │ ├── PromptInput.css │ │ └── PromptInput.tsx │ ├── PromptResponseList │ │ ├── PromptResponseList.css │ │ ├── PromptResponseList.tsx │ │ ├── chatgpt.png │ │ ├── me.png │ │ └── response-interface.ts │ ├── advancedChat │ │ ├── PureChat.css │ │ └── PureChat.tsx │ └── chatRoom.tsx ├── next.config.js ├── package-lock.json ├── package.json ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── api │ │ └── hello.ts │ ├── index.tsx │ ├── mainPage.tsx │ └── signupPage.tsx ├── public │ ├── bg-image.jpg │ ├── favicon.ico │ ├── next.svg │ ├── thirteen.svg │ └── vercel.svg ├── styles │ └── globals.css ├── tsconfig.json └── types │ └── html-docx-js.d.ts ├── README.md └── Springboot-MongoDB-backend ├── .idea ├── .gitignore ├── compiler.xml ├── encodings.xml ├── jarRepositories.xml ├── misc.xml └── vcs.xml ├── mongdb.iml ├── pom.xml └── src └── main └── java └── com └── mongdb ├── MongodbApplication.java ├── common ├── CorsConfig.java ├── JwtUtil.java ├── RabbitMQConfig.java ├── Result.java ├── SendGridConfig.java └── WebClientConfig.java ├── controller ├── ApiController.java ├── MessageController.java └── UserController.java ├── dao ├── EmailService.java ├── MessageDao.java ├── RedisService.java └── UserDao.java ├── listeners └── EmailListener.java └── model ├── ApiModel.java ├── ChatCompletionRequest.java ├── EmailMessage.java ├── MessageModel.java └── UserModel.java /Langchain/agent/__init__.py: -------------------------------------------------------------------------------- 1 | from agent.bing_search import bing_search 2 | -------------------------------------------------------------------------------- /Langchain/agent/bing_search.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | 3 | from langchain.utilities import BingSearchAPIWrapper 4 | from configs.model_config import BING_SEARCH_URL, BING_SUBSCRIPTION_KEY 5 | 6 | 7 | def bing_search(text, result_len=3): 8 | if not (BING_SEARCH_URL and BING_SUBSCRIPTION_KEY): 9 | return [{"snippet": "please set BING_SUBSCRIPTION_KEY and BING_SEARCH_URL in os ENV", 10 | "title": "env info is not found", 11 | "link": "https://python.langchain.com/en/latest/modules/agents/tools/examples/bing_search.html"}] 12 | search = BingSearchAPIWrapper(bing_subscription_key=BING_SUBSCRIPTION_KEY, 13 | bing_search_url=BING_SEARCH_URL) 14 | return search.results(text, result_len) 15 | 16 | 17 | if __name__ == "__main__": 18 | r = bing_search('python') 19 | print(r) 20 | -------------------------------------------------------------------------------- /Langchain/api.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import shutil 5 | from typing import List, Optional 6 | import urllib 7 | 8 | import nltk 9 | import pydantic 10 | import uvicorn 11 | from fastapi import Body, FastAPI, File, Form, Query, UploadFile, WebSocket 12 | from fastapi.middleware.cors import CORSMiddleware 13 | from pydantic import BaseModel 14 | from typing_extensions import Annotated 15 | from starlette.responses import RedirectResponse 16 | 17 | from chains.local_doc_qa import LocalDocQA 18 | from configs.model_config import (KB_ROOT_PATH, EMBEDDING_DEVICE, 19 | EMBEDDING_MODEL, NLTK_DATA_PATH, 20 | VECTOR_SEARCH_TOP_K, LLM_HISTORY_LEN, OPEN_CROSS_DOMAIN) 21 | import models.shared as shared 22 | from models.loader.args import parser 23 | from models.loader import LoaderCheckPoint 24 | 25 | nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path 26 | 27 | 28 | class BaseResponse(BaseModel): 29 | code: int = pydantic.Field(200, description="HTTP status code") 30 | msg: str = pydantic.Field("success", description="HTTP status message") 31 | 32 | class Config: 33 | schema_extra = { 34 | "example": { 35 | "code": 200, 36 | "msg": "success", 37 | } 38 | } 39 | 40 | 41 | class ListDocsResponse(BaseResponse): 42 | data: List[str] = pydantic.Field(..., description="List of document names") 43 | 44 | class Config: 45 | schema_extra = { 46 | "example": { 47 | "code": 200, 48 | "msg": "success", 49 | "data": ["doc1.docx", "doc2.pdf", "doc3.txt"], 50 | } 51 | } 52 | 53 | 54 | class ChatMessage(BaseModel): 55 | question: str = pydantic.Field(..., description="Question text") 56 | response: str = pydantic.Field(..., description="Response text") 57 | history: List[List[str]] = pydantic.Field(..., description="History text") 58 | source_documents: List[str] = pydantic.Field( 59 | ..., description="List of source documents and their scores" 60 | ) 61 | 62 | class Config: 63 | schema_extra = { 64 | "example": { 65 | "question": "工伤保险如何办理?", 66 | "response": "根据已知信息,可以总结如下:\n\n1. 参保单位为员工缴纳工伤保险费,以保障员工在发生工伤时能够获得相应的待遇。\n2. 不同地区的工伤保险缴费规定可能有所不同,需要向当地社保部门咨询以了解具体的缴费标准和规定。\n3. 工伤从业人员及其近亲属需要申请工伤认定,确认享受的待遇资格,并按时缴纳工伤保险费。\n4. 工伤保险待遇包括工伤医疗、康复、辅助器具配置费用、伤残待遇、工亡待遇、一次性工亡补助金等。\n5. 工伤保险待遇领取资格认证包括长期待遇领取人员认证和一次性待遇领取人员认证。\n6. 工伤保险基金支付的待遇项目包括工伤医疗待遇、康复待遇、辅助器具配置费用、一次性工亡补助金、丧葬补助金等。", 67 | "history": [ 68 | [ 69 | "工伤保险是什么?", 70 | "工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。", 71 | ] 72 | ], 73 | "source_documents": [ 74 | "出处 [1] 广州市单位从业的特定人员参加工伤保险办事指引.docx:\n\n\t( 一) 从业单位 (组织) 按“自愿参保”原则, 为未建 立劳动关系的特定从业人员单项参加工伤保险 、缴纳工伤保 险费。", 75 | "出处 [2] ...", 76 | "出处 [3] ...", 77 | ], 78 | } 79 | } 80 | 81 | 82 | def get_folder_path(local_doc_id: str): 83 | return os.path.join(KB_ROOT_PATH, local_doc_id, "content") 84 | 85 | 86 | def get_vs_path(local_doc_id: str): 87 | return os.path.join(KB_ROOT_PATH, local_doc_id, "vector_store") 88 | 89 | 90 | def get_file_path(local_doc_id: str, doc_name: str): 91 | return os.path.join(KB_ROOT_PATH, local_doc_id, "content", doc_name) 92 | 93 | 94 | async def upload_file( 95 | file: UploadFile = File(description="A single binary file"), 96 | knowledge_base_id: str = Form(..., description="Knowledge Base Name", example="kb1"), 97 | ): 98 | saved_path = get_folder_path(knowledge_base_id) 99 | if not os.path.exists(saved_path): 100 | os.makedirs(saved_path) 101 | 102 | file_content = await file.read() # 读取上传文件的内容 103 | 104 | file_path = os.path.join(saved_path, file.filename) 105 | if os.path.exists(file_path) and os.path.getsize(file_path) == len(file_content): 106 | file_status = f"文件 {file.filename} 已存在。" 107 | return BaseResponse(code=200, msg=file_status) 108 | 109 | with open(file_path, "wb") as f: 110 | f.write(file_content) 111 | 112 | vs_path = get_vs_path(knowledge_base_id) 113 | vs_path, loaded_files = local_doc_qa.init_knowledge_vector_store([file_path], vs_path) 114 | if len(loaded_files) > 0: 115 | file_status = f"文件 {file.filename} 已上传至新的知识库,并已加载知识库,请开始提问。" 116 | return BaseResponse(code=200, msg=file_status) 117 | else: 118 | file_status = "文件上传失败,请重新上传" 119 | return BaseResponse(code=500, msg=file_status) 120 | 121 | 122 | async def upload_files( 123 | files: Annotated[ 124 | List[UploadFile], File(description="Multiple files as UploadFile") 125 | ], 126 | knowledge_base_id: str = Form(..., description="Knowledge Base Name", example="kb1"), 127 | ): 128 | saved_path = get_folder_path(knowledge_base_id) 129 | if not os.path.exists(saved_path): 130 | os.makedirs(saved_path) 131 | filelist = [] 132 | for file in files: 133 | file_content = '' 134 | file_path = os.path.join(saved_path, file.filename) 135 | file_content = file.file.read() 136 | if os.path.exists(file_path) and os.path.getsize(file_path) == len(file_content): 137 | continue 138 | with open(file_path, "ab+") as f: 139 | f.write(file_content) 140 | filelist.append(file_path) 141 | if filelist: 142 | vs_path, loaded_files = local_doc_qa.init_knowledge_vector_store(filelist, get_vs_path(knowledge_base_id)) 143 | if len(loaded_files): 144 | file_status = f"documents {', '.join([os.path.split(i)[-1] for i in loaded_files])} upload success" 145 | return BaseResponse(code=200, msg=file_status) 146 | file_status = f"documents {', '.join([os.path.split(i)[-1] for i in loaded_files])} upload fail" 147 | return BaseResponse(code=500, msg=file_status) 148 | 149 | 150 | async def list_kbs(): 151 | # Get List of Knowledge Base 152 | if not os.path.exists(KB_ROOT_PATH): 153 | all_doc_ids = [] 154 | else: 155 | all_doc_ids = [ 156 | folder 157 | for folder in os.listdir(KB_ROOT_PATH) 158 | if os.path.isdir(os.path.join(KB_ROOT_PATH, folder)) 159 | and os.path.exists(os.path.join(KB_ROOT_PATH, folder, "vector_store", "index.faiss")) 160 | ] 161 | 162 | return ListDocsResponse(data=all_doc_ids) 163 | 164 | 165 | async def list_docs( 166 | knowledge_base_id: Optional[str] = Query(default=None, description="Knowledge Base Name", example="kb1") 167 | ): 168 | local_doc_folder = get_folder_path(knowledge_base_id) 169 | if not os.path.exists(local_doc_folder): 170 | return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"} 171 | all_doc_names = [ 172 | doc 173 | for doc in os.listdir(local_doc_folder) 174 | if os.path.isfile(os.path.join(local_doc_folder, doc)) 175 | ] 176 | return ListDocsResponse(data=all_doc_names) 177 | 178 | 179 | async def delete_kb( 180 | knowledge_base_id: str = Query(..., 181 | description="Knowledge Base Name", 182 | example="kb1"), 183 | ): 184 | # TODO: 确认是否支持批量删除知识库 185 | knowledge_base_id = urllib.parse.unquote(knowledge_base_id) 186 | if not os.path.exists(get_folder_path(knowledge_base_id)): 187 | return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"} 188 | shutil.rmtree(get_folder_path(knowledge_base_id)) 189 | return BaseResponse(code=200, msg=f"Knowledge Base {knowledge_base_id} delete success") 190 | 191 | 192 | async def delete_doc( 193 | knowledge_base_id: str = Query(..., 194 | description="Knowledge Base Name", 195 | example="kb1"), 196 | doc_name: str = Query( 197 | None, description="doc name", example="doc_name_1.pdf" 198 | ), 199 | ): 200 | knowledge_base_id = urllib.parse.unquote(knowledge_base_id) 201 | if not os.path.exists(get_folder_path(knowledge_base_id)): 202 | return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"} 203 | doc_path = get_file_path(knowledge_base_id, doc_name) 204 | if os.path.exists(doc_path): 205 | os.remove(doc_path) 206 | remain_docs = await list_docs(knowledge_base_id) 207 | if len(remain_docs.data) == 0: 208 | shutil.rmtree(get_folder_path(knowledge_base_id), ignore_errors=True) 209 | return BaseResponse(code=200, msg=f"document {doc_name} delete success") 210 | else: 211 | status = local_doc_qa.delete_file_from_vector_store(doc_path, get_vs_path(knowledge_base_id)) 212 | if "success" in status: 213 | return BaseResponse(code=200, msg=f"document {doc_name} delete success") 214 | else: 215 | return BaseResponse(code=1, msg=f"document {doc_name} delete fail") 216 | else: 217 | return BaseResponse(code=1, msg=f"document {doc_name} not found") 218 | 219 | 220 | async def update_doc( 221 | knowledge_base_id: str = Query(..., 222 | description="知识库名", 223 | example="kb1"), 224 | old_doc: str = Query( 225 | None, description="待删除文件名,已存储在知识库中", example="doc_name_1.pdf" 226 | ), 227 | new_doc: UploadFile = File(description="待上传文件"), 228 | ): 229 | knowledge_base_id = urllib.parse.unquote(knowledge_base_id) 230 | if not os.path.exists(get_folder_path(knowledge_base_id)): 231 | return {"code": 1, "msg": f"Knowledge base {knowledge_base_id} not found"} 232 | doc_path = get_file_path(knowledge_base_id, old_doc) 233 | if not os.path.exists(doc_path): 234 | return BaseResponse(code=1, msg=f"document {old_doc} not found") 235 | else: 236 | os.remove(doc_path) 237 | delete_status = local_doc_qa.delete_file_from_vector_store(doc_path, get_vs_path(knowledge_base_id)) 238 | if "fail" in delete_status: 239 | return BaseResponse(code=1, msg=f"document {old_doc} delete failed") 240 | else: 241 | saved_path = get_folder_path(knowledge_base_id) 242 | if not os.path.exists(saved_path): 243 | os.makedirs(saved_path) 244 | 245 | file_content = await new_doc.read() # 读取上传文件的内容 246 | 247 | file_path = os.path.join(saved_path, new_doc.filename) 248 | if os.path.exists(file_path) and os.path.getsize(file_path) == len(file_content): 249 | file_status = f"document {new_doc.filename} already exists" 250 | return BaseResponse(code=200, msg=file_status) 251 | 252 | with open(file_path, "wb") as f: 253 | f.write(file_content) 254 | 255 | vs_path = get_vs_path(knowledge_base_id) 256 | vs_path, loaded_files = local_doc_qa.init_knowledge_vector_store([file_path], vs_path) 257 | if len(loaded_files) > 0: 258 | file_status = f"document {old_doc} delete and document {new_doc.filename} upload success" 259 | return BaseResponse(code=200, msg=file_status) 260 | else: 261 | file_status = f"document {old_doc} success but document {new_doc.filename} upload fail" 262 | return BaseResponse(code=500, msg=file_status) 263 | 264 | 265 | 266 | async def local_doc_chat( 267 | knowledge_base_id: str = Body(..., description="Knowledge Base Name", example="kb1"), 268 | question: str = Body(..., description="Question", example="工伤保险是什么?"), 269 | history: List[List[str]] = Body( 270 | [], 271 | description="History of previous questions and answers", 272 | example=[ 273 | [ 274 | "工伤保险是什么?", 275 | "工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。", 276 | ] 277 | ], 278 | ), 279 | ): 280 | vs_path = get_vs_path(knowledge_base_id) 281 | if not os.path.exists(vs_path): 282 | # return BaseResponse(code=1, msg=f"Knowledge base {knowledge_base_id} not found") 283 | return ChatMessage( 284 | question=question, 285 | response=f"Knowledge base {knowledge_base_id} not found", 286 | history=history, 287 | source_documents=[], 288 | ) 289 | else: 290 | for resp, history in local_doc_qa.get_knowledge_based_answer( 291 | query=question, vs_path=vs_path, chat_history=history, streaming=True 292 | ): 293 | pass 294 | source_documents = [ 295 | f"""出处 [{inum + 1}] {os.path.split(doc.metadata['source'])[-1]}:\n\n{doc.page_content}\n\n""" 296 | f"""相关度:{doc.metadata['score']}\n\n""" 297 | for inum, doc in enumerate(resp["source_documents"]) 298 | ] 299 | 300 | return ChatMessage( 301 | question=question, 302 | response=resp["result"], 303 | history=history, 304 | source_documents=source_documents, 305 | ) 306 | 307 | 308 | async def bing_search_chat( 309 | question: str = Body(..., description="Question", example="工伤保险是什么?"), 310 | history: Optional[List[List[str]]] = Body( 311 | [], 312 | description="History of previous questions and answers", 313 | example=[ 314 | [ 315 | "工伤保险是什么?", 316 | "工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。", 317 | ] 318 | ], 319 | ), 320 | ): 321 | for resp, history in local_doc_qa.get_search_result_based_answer( 322 | query=question, chat_history=history, streaming=True 323 | ): 324 | pass 325 | source_documents = [ 326 | f"""出处 [{inum + 1}] [{doc.metadata["source"]}]({doc.metadata["source"]}) \n\n{doc.page_content}\n\n""" 327 | for inum, doc in enumerate(resp["source_documents"]) 328 | ] 329 | 330 | return ChatMessage( 331 | question=question, 332 | response=resp["result"], 333 | history=history, 334 | source_documents=source_documents, 335 | ) 336 | 337 | 338 | async def chat( 339 | question: str = Body(..., description="Question", example="工伤保险是什么?"), 340 | history: List[List[str]] = Body( 341 | [], 342 | description="History of previous questions and answers", 343 | example=[ 344 | [ 345 | "工伤保险是什么?", 346 | "工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。", 347 | ] 348 | ], 349 | ), 350 | ): 351 | for answer_result in local_doc_qa.llm.generatorAnswer(prompt=question, history=history, 352 | streaming=True): 353 | resp = answer_result.llm_output["answer"] 354 | history = answer_result.history 355 | pass 356 | 357 | return ChatMessage( 358 | question=question, 359 | response=resp, 360 | history=history, 361 | source_documents=[], 362 | ) 363 | 364 | 365 | async def stream_chat(websocket: WebSocket, knowledge_base_id: str): 366 | await websocket.accept() 367 | turn = 1 368 | while True: 369 | input_json = await websocket.receive_json() 370 | question, history, knowledge_base_id = input_json["question"], input_json["history"], input_json[ 371 | "knowledge_base_id"] 372 | vs_path = get_vs_path(knowledge_base_id) 373 | 374 | if not os.path.exists(vs_path): 375 | await websocket.send_json({"error": f"Knowledge base {knowledge_base_id} not found"}) 376 | await websocket.close() 377 | return 378 | 379 | await websocket.send_json({"question": question, "turn": turn, "flag": "start"}) 380 | 381 | last_print_len = 0 382 | for resp, history in local_doc_qa.get_knowledge_based_answer( 383 | query=question, vs_path=vs_path, chat_history=history, streaming=True 384 | ): 385 | await websocket.send_text(resp["result"][last_print_len:]) 386 | last_print_len = len(resp["result"]) 387 | 388 | source_documents = [ 389 | f"""出处 [{inum + 1}] {os.path.split(doc.metadata['source'])[-1]}:\n\n{doc.page_content}\n\n""" 390 | f"""相关度:{doc.metadata['score']}\n\n""" 391 | for inum, doc in enumerate(resp["source_documents"]) 392 | ] 393 | 394 | await websocket.send_text( 395 | json.dumps( 396 | { 397 | "question": question, 398 | "turn": turn, 399 | "flag": "end", 400 | "sources_documents": source_documents, 401 | }, 402 | ensure_ascii=False, 403 | ) 404 | ) 405 | turn += 1 406 | 407 | 408 | async def document(): 409 | return RedirectResponse(url="/docs") 410 | 411 | 412 | def api_start(host, port): 413 | global app 414 | global local_doc_qa 415 | 416 | llm_model_ins = shared.loaderLLM() 417 | llm_model_ins.set_history_len(LLM_HISTORY_LEN) 418 | 419 | app = FastAPI() 420 | # Add CORS middleware to allow all origins 421 | # 在config.py中设置OPEN_DOMAIN=True,允许跨域 422 | # set OPEN_DOMAIN=True in config.py to allow cross-domain 423 | if OPEN_CROSS_DOMAIN: 424 | app.add_middleware( 425 | CORSMiddleware, 426 | allow_origins=["*"], 427 | allow_credentials=True, 428 | allow_methods=["*"], 429 | allow_headers=["*"], 430 | ) 431 | app.websocket("/local_doc_qa/stream-chat/{knowledge_base_id}")(stream_chat) 432 | 433 | app.get("/", response_model=BaseResponse)(document) 434 | 435 | app.post("/chat", response_model=ChatMessage)(chat) 436 | 437 | app.post("/local_doc_qa/upload_file", response_model=BaseResponse)(upload_file) 438 | app.post("/local_doc_qa/upload_files", response_model=BaseResponse)(upload_files) 439 | app.post("/local_doc_qa/local_doc_chat", response_model=ChatMessage)(local_doc_chat) 440 | app.post("/local_doc_qa/bing_search_chat", response_model=ChatMessage)(bing_search_chat) 441 | app.get("/local_doc_qa/list_knowledge_base", response_model=ListDocsResponse)(list_kbs) 442 | app.get("/local_doc_qa/list_files", response_model=ListDocsResponse)(list_docs) 443 | app.delete("/local_doc_qa/delete_knowledge_base", response_model=BaseResponse)(delete_kb) 444 | app.delete("/local_doc_qa/delete_file", response_model=BaseResponse)(delete_doc) 445 | app.post("/local_doc_qa/update_file", response_model=BaseResponse)(update_doc) 446 | 447 | local_doc_qa = LocalDocQA() 448 | local_doc_qa.init_cfg( 449 | llm_model=llm_model_ins, 450 | embedding_model=EMBEDDING_MODEL, 451 | embedding_device=EMBEDDING_DEVICE, 452 | top_k=VECTOR_SEARCH_TOP_K, 453 | ) 454 | uvicorn.run(app, host=host, port=port) 455 | 456 | 457 | if __name__ == "__main__": 458 | parser.add_argument("--host", type=str, default="0.0.0.0") 459 | parser.add_argument("--port", type=int, default=7861) 460 | # 初始化消息 461 | args = None 462 | args = parser.parse_args() 463 | args_dict = vars(args) 464 | shared.loaderCheckPoint = LoaderCheckPoint(args_dict) 465 | api_start(args.host, args.port) 466 | -------------------------------------------------------------------------------- /Langchain/chains/dialogue_answering/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import ( 2 | DialogueWithSharedMemoryChains 3 | ) 4 | 5 | __all__ = [ 6 | "DialogueWithSharedMemoryChains" 7 | ] 8 | -------------------------------------------------------------------------------- /Langchain/chains/dialogue_answering/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import argparse 4 | import asyncio 5 | from argparse import Namespace 6 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../') 7 | from chains.dialogue_answering import * 8 | from langchain.llms import OpenAI 9 | from models.base import (BaseAnswer, 10 | AnswerResult) 11 | import models.shared as shared 12 | from models.loader.args import parser 13 | from models.loader import LoaderCheckPoint 14 | 15 | async def dispatch(args: Namespace): 16 | 17 | args_dict = vars(args) 18 | shared.loaderCheckPoint = LoaderCheckPoint(args_dict) 19 | llm_model_ins = shared.loaderLLM() 20 | if not os.path.isfile(args.dialogue_path): 21 | raise FileNotFoundError(f'Invalid dialogue file path for demo mode: "{args.dialogue_path}"') 22 | llm = OpenAI(temperature=0) 23 | dialogue_instance = DialogueWithSharedMemoryChains(zero_shot_react_llm=llm, ask_llm=llm_model_ins, params=args_dict) 24 | 25 | dialogue_instance.agent_chain.run(input="What did David say before, summarize it") 26 | 27 | 28 | if __name__ == '__main__': 29 | 30 | parser.add_argument('--dialogue-path', default='', type=str, help='dialogue-path') 31 | parser.add_argument('--embedding-model', default='', type=str, help='embedding-model') 32 | args = parser.parse_args(['--dialogue-path', '/home/dmeck/Downloads/log.txt', 33 | '--embedding-mode', '/media/checkpoint/text2vec-large-chinese/']) 34 | loop = asyncio.new_event_loop() 35 | asyncio.set_event_loop(loop) 36 | loop.run_until_complete(dispatch(args)) 37 | -------------------------------------------------------------------------------- /Langchain/chains/dialogue_answering/base.py: -------------------------------------------------------------------------------- 1 | from langchain.base_language import BaseLanguageModel 2 | from langchain.agents import ZeroShotAgent, Tool, AgentExecutor 3 | from langchain.memory import ConversationBufferMemory, ReadOnlySharedMemory 4 | from langchain.chains import LLMChain, RetrievalQA 5 | from langchain.embeddings.huggingface import HuggingFaceEmbeddings 6 | from langchain.prompts import PromptTemplate 7 | from langchain.text_splitter import CharacterTextSplitter 8 | from langchain.vectorstores import Chroma 9 | 10 | from loader import DialogueLoader 11 | from chains.dialogue_answering.prompts import ( 12 | DIALOGUE_PREFIX, 13 | DIALOGUE_SUFFIX, 14 | SUMMARY_PROMPT 15 | ) 16 | 17 | 18 | class DialogueWithSharedMemoryChains: 19 | zero_shot_react_llm: BaseLanguageModel = None 20 | ask_llm: BaseLanguageModel = None 21 | embeddings: HuggingFaceEmbeddings = None 22 | embedding_model: str = None 23 | vector_search_top_k: int = 6 24 | dialogue_path: str = None 25 | dialogue_loader: DialogueLoader = None 26 | device: str = None 27 | 28 | def __init__(self, zero_shot_react_llm: BaseLanguageModel = None, ask_llm: BaseLanguageModel = None, 29 | params: dict = None): 30 | self.zero_shot_react_llm = zero_shot_react_llm 31 | self.ask_llm = ask_llm 32 | params = params or {} 33 | self.embedding_model = params.get('embedding_model', 'GanymedeNil/text2vec-large-chinese') 34 | self.vector_search_top_k = params.get('vector_search_top_k', 6) 35 | self.dialogue_path = params.get('dialogue_path', '') 36 | self.device = 'cuda' if params.get('use_cuda', False) else 'cpu' 37 | 38 | self.dialogue_loader = DialogueLoader(self.dialogue_path) 39 | self._init_cfg() 40 | self._init_state_of_history() 41 | self.memory_chain, self.memory = self._agents_answer() 42 | self.agent_chain = self._create_agent_chain() 43 | 44 | def _init_cfg(self): 45 | model_kwargs = { 46 | 'device': self.device 47 | } 48 | self.embeddings = HuggingFaceEmbeddings(model_name=self.embedding_model, model_kwargs=model_kwargs) 49 | 50 | def _init_state_of_history(self): 51 | documents = self.dialogue_loader.load() 52 | text_splitter = CharacterTextSplitter(chunk_size=3, chunk_overlap=1) 53 | texts = text_splitter.split_documents(documents) 54 | docsearch = Chroma.from_documents(texts, self.embeddings, collection_name="state-of-history") 55 | self.state_of_history = RetrievalQA.from_chain_type(llm=self.ask_llm, chain_type="stuff", 56 | retriever=docsearch.as_retriever()) 57 | 58 | def _agents_answer(self): 59 | 60 | memory = ConversationBufferMemory(memory_key="chat_history") 61 | readonly_memory = ReadOnlySharedMemory(memory=memory) 62 | memory_chain = LLMChain( 63 | llm=self.ask_llm, 64 | prompt=SUMMARY_PROMPT, 65 | verbose=True, 66 | memory=readonly_memory, # use the read-only memory to prevent the tool from modifying the memory 67 | ) 68 | return memory_chain, memory 69 | 70 | def _create_agent_chain(self): 71 | dialogue_participants = self.dialogue_loader.dialogue.participants_to_export() 72 | tools = [ 73 | Tool( 74 | name="State of Dialogue History System", 75 | func=self.state_of_history.run, 76 | description=f"Dialogue with {dialogue_participants} - The answers in this section are very useful " 77 | f"when searching for chat content between {dialogue_participants}. Input should be a " 78 | f"complete question. " 79 | ), 80 | Tool( 81 | name="Summary", 82 | func=self.memory_chain.run, 83 | description="useful for when you summarize a conversation. The input to this tool should be a string, " 84 | "representing who will read this summary. " 85 | ) 86 | ] 87 | 88 | prompt = ZeroShotAgent.create_prompt( 89 | tools, 90 | prefix=DIALOGUE_PREFIX, 91 | suffix=DIALOGUE_SUFFIX, 92 | input_variables=["input", "chat_history", "agent_scratchpad"] 93 | ) 94 | 95 | llm_chain = LLMChain(llm=self.zero_shot_react_llm, prompt=prompt) 96 | agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True) 97 | agent_chain = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=self.memory) 98 | 99 | return agent_chain 100 | -------------------------------------------------------------------------------- /Langchain/chains/dialogue_answering/prompts.py: -------------------------------------------------------------------------------- 1 | from langchain.prompts.prompt import PromptTemplate 2 | 3 | 4 | SUMMARY_TEMPLATE = """This is a conversation between a human and a bot: 5 | 6 | {chat_history} 7 | 8 | Write a summary of the conversation for {input}: 9 | """ 10 | 11 | SUMMARY_PROMPT = PromptTemplate( 12 | input_variables=["input", "chat_history"], 13 | template=SUMMARY_TEMPLATE 14 | ) 15 | 16 | DIALOGUE_PREFIX = """Have a conversation with a human,Analyze the content of the conversation. 17 | You have access to the following tools: """ 18 | DIALOGUE_SUFFIX = """Begin! 19 | 20 | {chat_history} 21 | Question: {input} 22 | {agent_scratchpad}""" 23 | -------------------------------------------------------------------------------- /Langchain/chains/modules/embeddings.py: -------------------------------------------------------------------------------- 1 | from langchain.embeddings.huggingface import HuggingFaceEmbeddings 2 | 3 | from typing import Any, List 4 | 5 | 6 | class MyEmbeddings(HuggingFaceEmbeddings): 7 | def __init__(self, **kwargs: Any): 8 | super().__init__(**kwargs) 9 | 10 | def embed_documents(self, texts: List[str]) -> List[List[float]]: 11 | """Compute doc embeddings using a HuggingFace transformer model. 12 | 13 | Args: 14 | texts: The list of texts to embed. 15 | 16 | Returns: 17 | List of embeddings, one for each text. 18 | """ 19 | texts = list(map(lambda x: x.replace("\n", " "), texts)) 20 | embeddings = self.client.encode(texts, normalize_embeddings=True) 21 | return embeddings.tolist() 22 | 23 | def embed_query(self, text: str) -> List[float]: 24 | """Compute query embeddings using a HuggingFace transformer model. 25 | 26 | Args: 27 | text: The text to embed. 28 | 29 | Returns: 30 | Embeddings for the text. 31 | """ 32 | text = text.replace("\n", " ") 33 | embedding = self.client.encode(text, normalize_embeddings=True) 34 | return embedding.tolist() 35 | -------------------------------------------------------------------------------- /Langchain/chains/modules/vectorstores.py: -------------------------------------------------------------------------------- 1 | from langchain.vectorstores import FAISS 2 | from typing import Any, Callable, List, Optional, Tuple, Dict 3 | from langchain.docstore.document import Document 4 | from langchain.docstore.base import Docstore 5 | 6 | from langchain.vectorstores.utils import maximal_marginal_relevance 7 | from langchain.embeddings.base import Embeddings 8 | import uuid 9 | from langchain.docstore.in_memory import InMemoryDocstore 10 | 11 | import numpy as np 12 | 13 | def dependable_faiss_import() -> Any: 14 | """Import faiss if available, otherwise raise error.""" 15 | try: 16 | import faiss 17 | except ImportError: 18 | raise ValueError( 19 | "Could not import faiss python package. " 20 | "Please install it with `pip install faiss` " 21 | "or `pip install faiss-cpu` (depending on Python version)." 22 | ) 23 | return faiss 24 | 25 | class FAISSVS(FAISS): 26 | def __init__(self, 27 | embedding_function: Callable[..., Any], 28 | index: Any, 29 | docstore: Docstore, 30 | index_to_docstore_id: Dict[int, str]): 31 | super().__init__(embedding_function, index, docstore, index_to_docstore_id) 32 | 33 | def max_marginal_relevance_search_by_vector( 34 | self, embedding: List[float], k: int = 4, fetch_k: int = 20, **kwargs: Any 35 | ) -> List[Tuple[Document, float]]: 36 | """Return docs selected using the maximal marginal relevance. 37 | 38 | Maximal marginal relevance optimizes for similarity to query AND diversity 39 | among selected documents. 40 | 41 | Args: 42 | embedding: Embedding to look up documents similar to. 43 | k: Number of Documents to return. Defaults to 4. 44 | fetch_k: Number of Documents to fetch to pass to MMR algorithm. 45 | 46 | Returns: 47 | List of Documents with scores selected by maximal marginal relevance. 48 | """ 49 | scores, indices = self.index.search(np.array([embedding], dtype=np.float32), fetch_k) 50 | # -1 happens when not enough docs are returned. 51 | embeddings = [self.index.reconstruct(int(i)) for i in indices[0] if i != -1] 52 | mmr_selected = maximal_marginal_relevance( 53 | np.array([embedding], dtype=np.float32), embeddings, k=k 54 | ) 55 | selected_indices = [indices[0][i] for i in mmr_selected] 56 | selected_scores = [scores[0][i] for i in mmr_selected] 57 | docs = [] 58 | for i, score in zip(selected_indices, selected_scores): 59 | if i == -1: 60 | # This happens when not enough docs are returned. 61 | continue 62 | _id = self.index_to_docstore_id[i] 63 | doc = self.docstore.search(_id) 64 | if not isinstance(doc, Document): 65 | raise ValueError(f"Could not find document for id {_id}, got {doc}") 66 | docs.append((doc, score)) 67 | return docs 68 | 69 | def max_marginal_relevance_search( 70 | self, 71 | query: str, 72 | k: int = 4, 73 | fetch_k: int = 20, 74 | **kwargs: Any, 75 | ) -> List[Tuple[Document, float]]: 76 | """Return docs selected using the maximal marginal relevance. 77 | 78 | Maximal marginal relevance optimizes for similarity to query AND diversity 79 | among selected documents. 80 | 81 | Args: 82 | query: Text to look up documents similar to. 83 | k: Number of Documents to return. Defaults to 4. 84 | fetch_k: Number of Documents to fetch to pass to MMR algorithm. 85 | 86 | Returns: 87 | List of Documents with scores selected by maximal marginal relevance. 88 | """ 89 | embedding = self.embedding_function(query) 90 | docs = self.max_marginal_relevance_search_by_vector(embedding, k, fetch_k) 91 | return docs 92 | 93 | @classmethod 94 | def __from( 95 | cls, 96 | texts: List[str], 97 | embeddings: List[List[float]], 98 | embedding: Embeddings, 99 | metadatas: Optional[List[dict]] = None, 100 | **kwargs: Any, 101 | ) -> FAISS: 102 | faiss = dependable_faiss_import() 103 | index = faiss.IndexFlatIP(len(embeddings[0])) 104 | index.add(np.array(embeddings, dtype=np.float32)) 105 | 106 | # # my code, for speeding up search 107 | # quantizer = faiss.IndexFlatL2(len(embeddings[0])) 108 | # index = faiss.IndexIVFFlat(quantizer, len(embeddings[0]), 100) 109 | # index.train(np.array(embeddings, dtype=np.float32)) 110 | # index.add(np.array(embeddings, dtype=np.float32)) 111 | 112 | documents = [] 113 | for i, text in enumerate(texts): 114 | metadata = metadatas[i] if metadatas else {} 115 | documents.append(Document(page_content=text, metadata=metadata)) 116 | index_to_id = {i: str(uuid.uuid4()) for i in range(len(documents))} 117 | docstore = InMemoryDocstore( 118 | {index_to_id[i]: doc for i, doc in enumerate(documents)} 119 | ) 120 | return cls(embedding.embed_query, index, docstore, index_to_id) 121 | 122 | -------------------------------------------------------------------------------- /Langchain/chains/text_load.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pinecone 3 | from tqdm import tqdm 4 | from langchain.llms import OpenAI 5 | from langchain.text_splitter import SpacyTextSplitter 6 | from langchain.document_loaders import TextLoader 7 | from langchain.document_loaders import DirectoryLoader 8 | from langchain.indexes import VectorstoreIndexCreator 9 | from langchain.embeddings.openai import OpenAIEmbeddings 10 | from langchain.vectorstores import Pinecone 11 | 12 | #一些配置文件 13 | openai_key="你的key" # 注册 openai.com 后获得 14 | pinecone_key="你的key" # 注册 app.pinecone.io 后获得 15 | pinecone_index="你的库" #app.pinecone.io 获得 16 | pinecone_environment="你的Environment" # 登录pinecone后,在indexes页面 查看Environment 17 | pinecone_namespace="你的Namespace" #如果不存在自动创建 18 | 19 | #科学上网你懂得 20 | os.environ['HTTP_PROXY'] = 'http://127.0.0.1:7890' 21 | os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890' 22 | 23 | #初始化pinecone 24 | pinecone.init( 25 | api_key=pinecone_key, 26 | environment=pinecone_environment 27 | ) 28 | index = pinecone.Index(pinecone_index) 29 | 30 | #初始化OpenAI的embeddings 31 | embeddings = OpenAIEmbeddings(openai_api_key=openai_key) 32 | 33 | #初始化text_splitter 34 | text_splitter = SpacyTextSplitter(pipeline='zh_core_web_sm',chunk_size=1000,chunk_overlap=200) 35 | 36 | # 读取目录下所有后缀是txt的文件 37 | loader = DirectoryLoader('../docs', glob="**/*.txt", loader_cls=TextLoader) 38 | 39 | #读取文本文件 40 | documents = loader.load() 41 | 42 | # 使用text_splitter对文档进行分割 43 | split_text = text_splitter.split_documents(documents) 44 | try: 45 | for document in tqdm(split_text): 46 | # 获取向量并储存到pinecone 47 | Pinecone.from_documents([document], embeddings, index_name=pinecone_index) 48 | except Exception as e: 49 | print(f"Error: {e}") 50 | quit() 51 | 52 | 53 | -------------------------------------------------------------------------------- /Langchain/cli.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from api import api_start as api_start 4 | from cli_demo import main as cli_start 5 | from configs.model_config import llm_model_dict, embedding_model_dict 6 | 7 | 8 | @click.group() 9 | @click.version_option(version='1.0.0') 10 | @click.pass_context 11 | def cli(ctx): 12 | pass 13 | 14 | 15 | @cli.group() 16 | def llm(): 17 | pass 18 | 19 | 20 | @llm.command(name="ls") 21 | def llm_ls(): 22 | for k in llm_model_dict.keys(): 23 | print(k) 24 | 25 | 26 | @cli.group() 27 | def embedding(): 28 | pass 29 | 30 | 31 | @embedding.command(name="ls") 32 | def embedding_ls(): 33 | for k in embedding_model_dict.keys(): 34 | print(k) 35 | 36 | 37 | @cli.group() 38 | def start(): 39 | pass 40 | 41 | 42 | @start.command(name="api", context_settings=dict(help_option_names=['-h', '--help'])) 43 | @click.option('-i', '--ip', default='0.0.0.0', show_default=True, type=str, help='api_server listen address.') 44 | @click.option('-p', '--port', default=7861, show_default=True, type=int, help='api_server listen port.') 45 | def start_api(ip, port): 46 | # 调用api_start之前需要先loadCheckPoint,并传入加载检查点的参数, 47 | # 理论上可以用click包进行包装,但过于繁琐,改动较大, 48 | # 此处仍用parser包,并以models.loader.args.DEFAULT_ARGS的参数为默认参数 49 | # 如有改动需要可以更改models.loader.args.DEFAULT_ARGS 50 | from models import shared 51 | from models.loader import LoaderCheckPoint 52 | from models.loader.args import DEFAULT_ARGS 53 | shared.loaderCheckPoint = LoaderCheckPoint(DEFAULT_ARGS) 54 | api_start(host=ip, port=port) 55 | 56 | # # 通过cli.py调用cli_demo时需要在cli.py里初始化模型,否则会报错: 57 | # langchain-ChatGLM: error: unrecognized arguments: start cli 58 | # 为此需要先将 59 | # args = None 60 | # args = parser.parse_args() 61 | # args_dict = vars(args) 62 | # shared.loaderCheckPoint = LoaderCheckPoint(args_dict) 63 | # 语句从main函数里取出放到函数外部 64 | # 然后在cli.py里初始化 65 | 66 | @start.command(name="cli", context_settings=dict(help_option_names=['-h', '--help'])) 67 | def start_cli(): 68 | print("通过cli.py调用cli_demo...") 69 | 70 | from models import shared 71 | from models.loader import LoaderCheckPoint 72 | from models.loader.args import DEFAULT_ARGS 73 | shared.loaderCheckPoint = LoaderCheckPoint(DEFAULT_ARGS) 74 | cli_start() 75 | 76 | # 同cli命令,通过cli.py调用webui时,argparse的初始化需要放到cli.py里, 77 | # 但由于webui.py里,模型初始化通过init_model函数实现,也无法简单地分离出主函数, 78 | # 因此除非对webui进行大改,否则无法通过python cli.py start webui 调用webui。 79 | # 故建议不要通过以上命令启动webui,将下述语句注释掉 80 | 81 | @start.command(name="webui", context_settings=dict(help_option_names=['-h', '--help'])) 82 | def start_webui(): 83 | import webui 84 | 85 | 86 | cli() 87 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | 4 | } 5 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 18 | 19 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 20 | 21 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 22 | 23 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 24 | 25 | ## Learn More 26 | 27 | To learn more about Next.js, take a look at the following resources: 28 | 29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 31 | 32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 33 | 34 | ## Deploy on Vercel 35 | 36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 37 | 38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 39 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/PromptInput/PromptInput.css: -------------------------------------------------------------------------------- 1 | /* Style for the prompt input */ 2 | #prompt-input { 3 | flex-grow: 1; 4 | padding: 10px; 5 | border-radius: 5px; 6 | min-height: 20px; 7 | color: #333; 8 | overflow: auto; 9 | background-color: #f5f5f5; 10 | border: 1px solid #d3d3d3; 11 | } 12 | 13 | #prompt-input:focus { 14 | outline: none !important; 15 | } 16 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/PromptInput/PromptInput.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useCallback } from 'react'; 2 | import ContentEditable from 'react-contenteditable'; 3 | import './PromptInput.css'; 4 | import ParticlesBg from 'particles-bg'; 5 | 6 | interface PromptInputProps { 7 | prompt: string; 8 | onSubmit: () => void; 9 | updatePrompt: (prompt: string) => void; 10 | } 11 | 12 | const PromptInput: React.FC = ({ prompt, onSubmit, updatePrompt }) => { 13 | const checkKeyPress = useCallback((e: KeyboardEvent) => { 14 | if (e.key === 'Enter') { 15 | e.preventDefault(); 16 | if (e.ctrlKey || e.shiftKey) { 17 | document.execCommand('insertHTML', false, '

'); 18 | } else { 19 | onSubmit(); 20 | } 21 | } 22 | // eslint-disable-next-line react-hooks/exhaustive-deps 23 | }, [prompt]); 24 | 25 | const contentEditableRef = useRef(null); 26 | 27 | useEffect(() => { 28 | window.addEventListener("keydown", checkKeyPress); 29 | return () => { 30 | window.removeEventListener("keydown", checkKeyPress); 31 | }; 32 | }, [checkKeyPress]); 33 | 34 | return ( 35 | updatePrompt(event.target.value)} 42 | /> 43 | ); 44 | }; 45 | 46 | export default PromptInput; 47 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/PromptResponseList/PromptResponseList.css: -------------------------------------------------------------------------------- 1 | /* Style for each response element in the list */ 2 | .response-container { 3 | margin-bottom: 0; /* Remove gap between messages */ 4 | color: white; 5 | padding: 15px 200px; 6 | font-size: 1rem; 7 | display: flex; 8 | } 9 | 10 | .response-container .avatar-image { 11 | width: 30px; 12 | height: 30px; 13 | margin-right: 15px; 14 | } 15 | 16 | .response-container .response-content { 17 | display: flex; 18 | flex-direction: column; 19 | } 20 | 21 | .response-container pre { 22 | max-width: 100%; 23 | margin: 0 !important; 24 | white-space: break-spaces; 25 | } 26 | 27 | .response-container .prompt-content { 28 | background: transparent !important; 29 | color: #333; 30 | padding: 0 !important; 31 | margin-top: 5px; 32 | } 33 | 34 | .response-container .prompt-content p:first-child { 35 | margin-top: 0; 36 | } 37 | 38 | .ai-image { 39 | width: 500px; 40 | height: auto; 41 | } 42 | 43 | .error-response { 44 | color: rgb(220, 0, 0) !important; 45 | } 46 | 47 | /* Override hljs to match for chatgpt */ 48 | .hljs { 49 | background: rgb(255,255,255) !important; 50 | color: #333 !important; 51 | display: block; 52 | padding: 10px; 53 | border-radius: 6px; 54 | } 55 | 56 | .hljs-section, .hljs-title { 57 | color: #f22c3d !important; 58 | } 59 | 60 | .hljs-deletion, .hljs-number, .hljs-quote, .hljs-selector-class, .hljs-selector-id, .hljs-string, .hljs-template-tag, .hljs-type { 61 | color: #df3079 !important; 62 | } 63 | 64 | .hljs-addition, .hljs-built_in, .hljs-bullet, .hljs-code { 65 | color: #e9950c !important; 66 | } 67 | 68 | .hljs-link, .hljs-operator, .hljs-regexp, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-symbol, .hljs-template-variable, .hljs-variable { 69 | color: #333 !important; 70 | } 71 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/PromptResponseList/PromptResponseList.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC, useEffect, useRef} from 'react'; 2 | 3 | // @ts-ignore 4 | import ChatGptImg from './chatgpt.png'; 5 | // @ts-ignore 6 | import MyImg from './me.png'; 7 | import ReactMarkdown from 'react-markdown'; 8 | import {ResponseInterface} from "./response-interface"; 9 | import hljs from 'highlight.js'; 10 | import './PromptResponseList.css'; 11 | 12 | interface PromptResponseListProps { 13 | responseList: ResponseInterface[]; 14 | } 15 | 16 | const PromptResponseList: FC = ({ responseList }) => { 17 | const responseListRef = useRef(null); 18 | 19 | useEffect(() => { 20 | hljs.highlightAll(); 21 | }) 22 | 23 | useEffect(() => { 24 | hljs.highlightAll(); 25 | }, [responseList]); 26 | 27 | return ( 28 |
29 | {responseList.map((responseData) => ( 30 |
31 | avatar 32 |
33 | { responseData.image && 34 | generated ai 35 | } 36 | { responseData.response && 37 | 43 | {children} 44 | 45 | ) 46 | } 47 | }} 48 | /> 49 | } 50 |
51 |
52 | ))} 53 |
54 | ); 55 | }; 56 | 57 | export default PromptResponseList; 58 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/PromptResponseList/chatgpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceDavy/ChatNote/e99d32ee0b4f2fab1fd216614fcc7dfaa7bc1357/Nextjs-Reactjs-frontend/components/PromptResponseList/chatgpt.png -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/PromptResponseList/me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceDavy/ChatNote/e99d32ee0b4f2fab1fd216614fcc7dfaa7bc1357/Nextjs-Reactjs-frontend/components/PromptResponseList/me.png -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/PromptResponseList/response-interface.ts: -------------------------------------------------------------------------------- 1 | export interface ResponseInterface { 2 | id: string; 3 | response?: string; 4 | selfFlag: boolean; 5 | error?: boolean; 6 | image?: string; 7 | } 8 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/advancedChat/PureChat.css: -------------------------------------------------------------------------------- 1 | /*html, body, #root, .App {*/ 2 | /* height: 100%;*/ 3 | /* margin:0;*/ 4 | /*}*/ 5 | 6 | 7 | 8 | 9 | .Wrapper { 10 | background-color: rgba(63, 181, 171, 0.25); 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | min-height: 100vh; 15 | } 16 | 17 | .PureChat { 18 | display: flex; 19 | flex-direction: column; 20 | height: 95%; 21 | width: 90%; 22 | font-family: 'Roboto', sans-serif; 23 | overflow: hidden; 24 | } 25 | 26 | 27 | #response-list { 28 | border: none; 29 | overflow-y: auto; 30 | max-height: 75vh; 31 | min-height: 75vh; 32 | flex: 1; 33 | background-color: rgb(245, 245, 245); 34 | border-radius: 10px; 35 | padding: 0; /* Adjust padding here */ 36 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 37 | margin-bottom: 10px; 38 | } 39 | 40 | .chatgpt-response { 41 | background-color: #f5f5f5; 42 | padding: 10px; 43 | border-radius: 5px; 44 | margin-bottom: 0; /* Remove gap between messages */ 45 | } 46 | 47 | .my-question { 48 | background-color: #e0e0e0; 49 | padding: 10px; 50 | border-radius: 5px; 51 | margin-bottom: 0; /* Remove gap between messages */ 52 | } 53 | 54 | 55 | #chat-container { 56 | display: flex; 57 | flex-direction: column; 58 | flex: 0 0 auto; 59 | } 60 | 61 | #regenerate-button-container { 62 | display: flex; 63 | flex-direction: row; 64 | justify-content: center; 65 | margin-bottom: 20px; 66 | } 67 | 68 | 69 | 70 | #regenerate-response-button { 71 | color: white; 72 | border: none; 73 | background: #10A37F; 74 | border-radius: 4px; 75 | padding: 10px 20px; 76 | cursor: pointer; 77 | font-weight: 500; 78 | } 79 | 80 | #model-select-container { 81 | margin: 0 0 20px 0; 82 | color: rgb(63, 150, 181); 83 | display: flex; 84 | align-items: center; 85 | justify-content: space-between; 86 | flex-wrap: wrap; 87 | } 88 | 89 | #model-select-container label { 90 | font-weight: 500; 91 | } 92 | 93 | #model-select-container select { 94 | background: white; 95 | border: 1px solid #d3d3d3; 96 | outline: none; 97 | color: #333; 98 | padding: 5px; 99 | border-radius: 5px; 100 | } 101 | 102 | #model-select-container select option:not(:checked) { 103 | background: rgba(63, 181, 171, 0.25); 104 | } 105 | 106 | #model-select-container select option { 107 | background: rgba(32,33,35,.5); 108 | } 109 | 110 | #input-container { 111 | display: flex; 112 | align-items: center; 113 | justify-content: space-between; 114 | padding: 10px; 115 | background-color: white; 116 | border: 1px solid #d3d3d3; 117 | border-radius: 5px; 118 | } 119 | 120 | #submit-button { 121 | background: transparent url("data:image/svg+xml,") no-repeat center center; 122 | color: white; 123 | width: 40px; 124 | height: 40px; 125 | border: none; 126 | border-radius: 5px; 127 | cursor: pointer; 128 | } 129 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/advancedChat/PureChat.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import axios from "axios"; 3 | import PromptInput from "../PromptInput/PromptInput"; 4 | import './PureChat.css'; 5 | import {ResponseInterface} from "../PromptResponseList/response-interface"; 6 | import PromptResponseList from "../PromptResponseList/PromptResponseList"; 7 | import MyToolbar from "../ToolBar"; 8 | import ParticlesBg from "particles-bg"; 9 | type ModelValueType = 'gpt' | 'codex' | 'image'; 10 | const PureChat = () => { 11 | 12 | const [responseList, setResponseList] = useState([]); 13 | const [prompt, setPrompt] = useState(''); 14 | const [promptToRetry, setPromptToRetry] = useState(null); 15 | const [uniqueIdToRetry, setUniqueIdToRetry] = useState(null); 16 | const [modelValue, setModelValue] = useState('gpt'); 17 | const [isLoading, setIsLoading] = useState(false); 18 | let loadInterval: number | undefined; 19 | 20 | const generateUniqueId = () => { 21 | const timestamp = Date.now(); 22 | const randomNumber = Math.random(); 23 | const hexadecimalString = randomNumber.toString(16); 24 | 25 | return `id-${timestamp}-${hexadecimalString}`; 26 | } 27 | 28 | const htmlToText = (html: string) => { 29 | const temp = document.createElement('div'); 30 | temp.innerHTML = html; 31 | return temp.textContent; 32 | } 33 | 34 | const delay = (ms: number) => { 35 | return new Promise( resolve => setTimeout(resolve, ms) ); 36 | } 37 | 38 | const addLoader = (uid: string) => { 39 | const element = document.getElementById(uid) as HTMLElement; 40 | element.textContent = '' 41 | 42 | // @ts-ignore 43 | loadInterval = setInterval(() => { 44 | // Update the text content of the loading indicator 45 | element.textContent += '.'; 46 | 47 | // If the loading indicator has reached three dots, reset it 48 | if (element.textContent === '....') { 49 | element.textContent = ''; 50 | } 51 | }, 300); 52 | } 53 | 54 | 55 | const addResponse = (selfFlag: boolean, response?: string) => { 56 | const uid = generateUniqueId() 57 | setResponseList(prevResponses => [ 58 | ...prevResponses, 59 | { 60 | id: uid, 61 | response, 62 | selfFlag 63 | }, 64 | ]); 65 | return uid; 66 | } 67 | 68 | const updateResponse = (uid: string, updatedObject: Record) => { 69 | setResponseList(prevResponses => { 70 | const updatedList = [...prevResponses] 71 | const index = prevResponses.findIndex((response) => response.id === uid); 72 | if (index > -1) { 73 | updatedList[index] = { 74 | ...updatedList[index], 75 | ...updatedObject 76 | } 77 | } 78 | return updatedList; 79 | }); 80 | } 81 | 82 | const regenerateResponse = async () => { 83 | await getGPTResult(promptToRetry, uniqueIdToRetry); 84 | } 85 | 86 | const getGPTResult = async (_promptToRetry?: string | null, _uniqueIdToRetry?: string | null) => { 87 | // Get the prompt input 88 | const _prompt = _promptToRetry ?? htmlToText(prompt); 89 | 90 | // If a response is already being generated or the prompt is empty, return 91 | if (isLoading || !_prompt) { 92 | return; 93 | } 94 | 95 | setIsLoading(true); 96 | 97 | // Clear the prompt input 98 | setPrompt(''); 99 | 100 | let uniqueId: string; 101 | if (_uniqueIdToRetry) { 102 | uniqueId = _uniqueIdToRetry; 103 | } else { 104 | // Add the self prompt to the response list 105 | addResponse(true, _prompt); 106 | uniqueId = addResponse(false); 107 | await delay(50); 108 | addLoader(uniqueId); 109 | } 110 | 111 | try { 112 | // Send a POST request to the API with the prompt in the request body 113 | const response = await axios.post('http://175.24.204.121:9091/get-prompt-result', { 114 | prompt: _prompt, 115 | model: modelValue 116 | }); 117 | if (modelValue === 'image') { 118 | // Show image for `Create image` model 119 | updateResponse(uniqueId, { 120 | image: response.data, 121 | }); 122 | } else { 123 | updateResponse(uniqueId, { 124 | response: response.data.trim(), 125 | }); 126 | } 127 | 128 | setPromptToRetry(null); 129 | setUniqueIdToRetry(null); 130 | } catch (err) { 131 | setPromptToRetry(_prompt); 132 | setUniqueIdToRetry(uniqueId); 133 | updateResponse(uniqueId, { 134 | // @ts-ignore 135 | response: `Error: ${err.message}`, 136 | error: true 137 | }); 138 | } finally { 139 | // Clear the loader interval 140 | clearInterval(loadInterval); 141 | setIsLoading(false); 142 | } 143 | } 144 | 145 | return ( 146 | 147 |
148 | 149 |
150 | 151 |
152 | 153 |
154 | 155 |
156 | { uniqueIdToRetry && 157 | (
158 | 161 |
162 | ) 163 | } 164 |
165 | 166 | 172 |
173 |
174 | getGPTResult()} 177 | key="prompt-input" 178 | updatePrompt={(prompt) => setPrompt(prompt)} 179 | /> 180 | 181 |
182 |
183 |
184 |
185 | ); 186 | } 187 | 188 | export default PureChat; 189 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/components/chatRoom.tsx: -------------------------------------------------------------------------------- 1 | 2 | import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; 3 | import React, { useState } from 'react'; 4 | import { MainContainer, ChatContainer, MessageList, Message, MessageInput, TypingIndicator, MessageModel } from '@chatscope/chat-ui-kit-react'; 5 | 6 | 7 | const API_KEY = "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"; 8 | const systemMessage = { 9 | "role": "system", "content": "Explain things as best as you can" 10 | } 11 | 12 | interface MessageObject { 13 | message: string; 14 | sentTime?: string; 15 | sender: string; 16 | direction?: string; 17 | } 18 | 19 | const Chatroom: React.FC = () => { 20 | const [messages, setMessages] = useState([ 21 | { 22 | message: "Hello, I'm DCHAT! Ask me anything!", 23 | sentTime: "just now", 24 | sender: "ChatGPT" 25 | } 26 | ]); 27 | const [isTyping, setIsTyping] = useState(false); 28 | type MessageDirection = 'incoming' | 'outgoing'; 29 | const handleSend = async (message: string) => { 30 | const newMessage: MessageObject = { 31 | message, 32 | direction: 'outgoing', 33 | sender: "user" 34 | }; 35 | 36 | const newMessages = [...messages, newMessage]; 37 | 38 | setMessages(newMessages); 39 | 40 | setIsTyping(true); 41 | await processMessageToChatGPT(newMessages); 42 | }; 43 | 44 | async function processMessageToChatGPT(chatMessages: MessageObject[]) { 45 | let apiMessages = chatMessages.map((messageObject) => { 46 | let role = messageObject.sender === "ChatGPT" ? "assistant" : "user"; 47 | return { role: role, content: messageObject.message } 48 | }); 49 | 50 | const apiRequestBody = { 51 | "model": "gpt-3.5-turbo", 52 | "messages": [systemMessage, ...apiMessages], 53 | 54 | }; 55 | 56 | try { 57 | const response = await fetch("https://api.openai.com/v1/chat/completions", { 58 | method: "POST", 59 | headers: { 60 | "Authorization": "Bearer " + API_KEY, 61 | "Content-Type": "application/json" 62 | }, 63 | body: JSON.stringify(apiRequestBody) 64 | }); 65 | 66 | if (!response.ok) { 67 | throw new Error(`HTTP error ${response.status}`); 68 | } 69 | if (response.body === null) { 70 | 71 | throw new Error("Response body is null"); 72 | } 73 | 74 | 75 | const reader = response.body.getReader(); 76 | const decoder = new TextDecoder("utf-8"); 77 | 78 | let result = ""; 79 | let done = false; 80 | while (!done) { 81 | const { value, done: streamDone } = await reader.read(); 82 | if (streamDone) { 83 | done = true; 84 | } else { 85 | result += decoder.decode(value); 86 | // Process the streamed data here, e.g., update the UI 87 | // You may need to parse the result and extract the relevant information 88 | } 89 | } 90 | 91 | const data = JSON.parse(result); 92 | console.log(data); 93 | setMessages([...chatMessages, { 94 | message: data.choices[0].message.content, 95 | sender: "ChatGPT" 96 | }]); 97 | } catch (error) { 98 | console.error("Error fetching data:", error); 99 | setMessages([...chatMessages, { 100 | message: "An error occurred. Please Check your network. Enable VPN if necessary.", 101 | sender: "ChatGPT" 102 | }]); 103 | } finally { 104 | setIsTyping(false); 105 | } 106 | } 107 | 108 | return ( 109 |
110 |
111 | 112 | 120 | 121 | 128 | : null} 139 | > 140 | {messages.map((message, i) => { 141 | const messageModel: MessageModel = { 142 | message: message.message, 143 | sentTime: message.sentTime, 144 | sender: message.sender, 145 | direction: message.direction as MessageDirection || 'incoming', 146 | position: 'single', 147 | }; 148 | 149 | return ; 150 | })} 151 | 152 | 159 | 160 | 161 | 162 |
163 | 164 | 165 | 166 | 167 |
168 | ) 169 | } 170 | 171 | export default Chatroom; 172 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const TerserPlugin = require("terser-webpack-plugin"); 3 | 4 | module.exports = { 5 | reactStrictMode: false, 6 | webpack: (config, { isServer, dev }) => { 7 | if (!dev && !isServer) { 8 | config.optimization.minimizer = [ 9 | new TerserPlugin({ 10 | terserOptions: { 11 | compress: { 12 | global_defs: { 13 | "typeof window": "object", 14 | }, 15 | }, 16 | ecma: 5, 17 | module: false, 18 | keep_classnames: false, 19 | keep_fnames: false, 20 | ie8: false, 21 | nameCache: null, 22 | safari10: false, 23 | toplevel: false, 24 | warnings: false, 25 | }, 26 | }), 27 | ]; 28 | } 29 | 30 | return config; 31 | }, 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dchat_ts_next", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@chatscope/chat-ui-kit-react": "^1.10.1", 13 | "@emotion/react": "^11.10.6", 14 | "@emotion/styled": "^11.10.6", 15 | "@mui/icons-material": "^5.11.11", 16 | "@mui/material": "^5.11.13", 17 | "@types/node": "18.15.5", 18 | "@types/react": "18.0.28", 19 | "@types/react-dom": "18.0.11", 20 | "eslint": "8.36.0", 21 | "eslint-config-next": "13.2.4", 22 | "file-saver": "^2.0.5", 23 | "html-docx-js": "^0.3.1", 24 | "html-to-pdfmake": "^2.4.16", 25 | "js-md5": "^0.7.3", 26 | "jspdf": "^2.5.1", 27 | "next": "13.2.4", 28 | "particles-bg": "^2.5.5", 29 | "pdfmake": "^0.2.7", 30 | "react": "18.2.0", 31 | "react-dom": "18.2.0", 32 | "react-loadable": "^5.5.0", 33 | "react-quill": "^2.0.0", 34 | "react-spring": "^9.7.1", 35 | "terser-webpack-plugin": "^5.3.7", 36 | "typescript": "5.0.2" 37 | }, 38 | "devDependencies": { 39 | "@types/file-saver": "^2.0.5", 40 | "@types/html-docx-js": "^0.3.1", 41 | "@types/html-to-pdfmake": "^2.4.0", 42 | "@types/js-md5": "^0.7.0", 43 | "@types/pdfmake": "^0.2.2", 44 | "@types/react-loadable": "^5.5.6" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css' 2 | import { AppProps } from 'next/app'; 3 | 4 | function MyApp({ Component, pageProps }: AppProps) { 5 | return ; 6 | } 7 | 8 | export default MyApp; 9 | 10 | // Disable strict mode 11 | MyApp.defaultProps = { 12 | ...MyApp.defaultProps, 13 | strictMode: false, 14 | }; 15 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Image from 'next/image' 3 | import { Inter } from 'next/font/google' 4 | // import styles from '@/styles/Home.module.css' 5 | import React, { useState } from 'react'; 6 | import { ReactNode } from 'react'; 7 | import { 8 | Container, 9 | TextField, 10 | Typography, 11 | Button, 12 | Link, 13 | Stack, 14 | Avatar, 15 | Paper, 16 | } from '@mui/material'; 17 | import { styled } from '@mui/system'; 18 | import { LockOutlined } from '@mui/icons-material'; 19 | import { useRouter } from 'next/router'; 20 | import dynamic from 'next/dynamic'; 21 | import { useSpring, animated } from 'react-spring'; 22 | 23 | 24 | import md5 from "js-md5"; 25 | 26 | import ParticlesBg, { Props } from 'particles-bg'; 27 | 28 | const DynamicParticlesBg = dynamic(() => import('particles-bg').then((mod) => mod.default), { 29 | ssr: false, 30 | }); 31 | 32 | 33 | export const BackgroundWrapper = styled('div')({ 34 | minHeight: '100vh', 35 | display: 'flex', 36 | justifyContent: 'center', 37 | alignItems: 'center', 38 | backgroundImage: `url('/bg-image.jpg')`, 39 | backgroundSize: '150% 150%', 40 | backgroundPosition: 'center', 41 | animation: 'backgroundMove 30s infinite alternate', 42 | '@keyframes backgroundMove': { 43 | '0%': { 44 | backgroundPosition: 'left top', 45 | }, 46 | '50%': { 47 | backgroundPosition: 'right bottom', 48 | }, 49 | '100%': { 50 | backgroundPosition: 'left top', 51 | }, 52 | }, 53 | }); 54 | export const WebsiteTitle = styled(Typography)(({ theme }) => ({ 55 | fontFamily: "'Roboto', sans-serif", 56 | fontSize: '2.5rem', 57 | fontWeight: 600, 58 | color: 'rgb(45,29,29)', 59 | textAlign: 'center', 60 | textShadow: '2px 2px 4px rgba(0, 0, 0, 0.5)', 61 | marginBottom: theme.spacing(4), 62 | })); 63 | 64 | export const StyledContainer = styled(Container)(({ theme }) => ({ 65 | display: 'flex', 66 | flexDirection: 'column', 67 | alignItems: 'center', 68 | padding: theme.spacing(2), 69 | borderRadius: theme.shape.borderRadius, 70 | 71 | })); 72 | 73 | export const StyledPaper = styled(Paper)(({ theme }) => ({ 74 | padding: theme.spacing(4), 75 | display: 'flex', 76 | flexDirection: 'column', 77 | alignItems: 'center', 78 | borderRadius: theme.shape.borderRadius * 2, 79 | boxShadow: '0 5px 15px rgba(0, 0, 0, 0.3)', 80 | backgroundColor: 'rgba(255, 255, 255, 0.6)', // Increase transparency by reducing the alpha value (0.6) 81 | })); 82 | 83 | export const StyledForm = styled('form')(({ theme }) => ({ 84 | width: '100%', 85 | marginTop: theme.spacing(1), 86 | })); 87 | 88 | export const StyledButton = styled(Button)(({ theme }) => ({ 89 | margin: theme.spacing(3, 0, 2), 90 | })); 91 | 92 | interface CopyrightProps { 93 | [key: string]: ReactNode; 94 | } 95 | 96 | function Copyright(props: CopyrightProps) { 97 | return ( 98 | 99 | {'Copyright © '} 100 | 101 | ChatNote powered by ChatGPT 102 | {' '} 103 | {new Date().getFullYear()} 104 | {'.'} 105 | 106 | ); 107 | } 108 | 109 | const LoginPage: React.FC = () => { 110 | const [email, setEmail] = useState(''); 111 | const [password, setPassword] = useState(''); 112 | const router = useRouter() 113 | 114 | const handleSubmit = async (e: React.FormEvent) => { 115 | 116 | e.preventDefault(); 117 | let encryptedPassword = md5(password); 118 | const user = { email, encryptedPassword } 119 | fetch(`http://175.24.204.121:9091/login`, { 120 | method: "POST", 121 | mode: 'cors', 122 | headers: { "Content-Type": "application/json" }, 123 | body: JSON.stringify(user) 124 | 125 | }).then(res => res.json()) 126 | .then((res) => { 127 | if (res.code === '1') { 128 | alert("signin successfully"); 129 | router.push({ 130 | pathname: '/mainPage', 131 | query: { mail: res.data.email }, 132 | }); 133 | // nav('/userLayout', {state: {mail: res.data.email}}); 134 | } 135 | else { 136 | alert("sign in failed") 137 | } 138 | 139 | }) 140 | 141 | }; 142 | 143 | const formAnimation : any = useSpring({ 144 | from: { opacity: 0, transform: 'translate3d(0, -50px, 0)' }, 145 | to: { opacity: 1, transform: 'translate3d(0, 0, 0)' }, 146 | config: { duration: 1000 }, 147 | }) ; 148 | 149 | return ( 150 | 151 | 152 | 153 | 154 | ChatNote 155 | 156 | 157 | 158 | 159 | 160 | Sign in 161 | 162 | 163 | setEmail(e.target.value)} 174 | /> 175 | setPassword(e.target.value)} 185 | /> 186 | 187 | Sign in 188 | 189 | 190 | 191 | Forgot your password? 192 | 193 | 194 | Dont have an account? 195 | 196 | 197 | 198 | Dont wanna sign in? fine. Click here to enter ^ ^ 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | ); 208 | } 209 | 210 | export default LoginPage; 211 | 212 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/pages/mainPage.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useRef, useCallback } from 'react'; 2 | 3 | import Chatroom from "../components/chatRoom"; 4 | import { Inter } from 'next/font/google' 5 | import { 6 | AppBar, 7 | Toolbar, 8 | Typography, 9 | Box, 10 | TextField, 11 | Button, 12 | Paper, 13 | Divider, CircularProgress, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, 14 | } from '@mui/material'; 15 | import { styled } from '@mui/system'; 16 | import { IconButton, Menu, MenuItem } from '@mui/material'; 17 | import PersonIcon from '@mui/icons-material/Person'; 18 | import 'react-quill/dist/quill.snow.css'; 19 | import GetAppIcon from '@mui/icons-material/GetApp'; 20 | import { useRouter } from "next/router"; 21 | import Loadable from 'react-loadable'; 22 | import pdfFonts from 'pdfmake/build/vfs_fonts'; 23 | import {saveAs} from "file-saver"; 24 | import pdfMake from 'pdfmake/build/pdfmake'; 25 | import htmlToPdfmake from 'html-to-pdfmake'; 26 | import PropTypes from "prop-types"; 27 | import htmlDocx from 'html-docx-js/dist/html-docx'; 28 | import { Content, TDocumentDefinitions } from 'pdfmake/interfaces'; 29 | import jsPDF from 'jspdf'; 30 | const LoadableReactQuill = Loadable({ 31 | loader: () => import('react-quill'), 32 | loading: () => , 33 | }); 34 | 35 | // const LoadableHtmlDocx = Loadable({ 36 | // loader: () => import('html-docx-js/dist/html-docx'), 37 | // loading: () => , 38 | // }); 39 | 40 | // const LoadableFileSaver = Loadable<{ saveAs: typeof import('file-saver').saveAs }>({ 41 | // loader: async () => { 42 | // const { saveAs } = await import('file-saver'); 43 | // return { saveAs }; 44 | // }, 45 | // loading: () => , 46 | // }); 47 | // const LoadableJsPDF = Loadable({ 48 | // loader: () => import('jspdf'), 49 | // loading: () => , 50 | // }); 51 | // 52 | // const LoadablePdfMake = Loadable({ 53 | // loader: () => import('pdfmake/build/pdfmake'), 54 | // loading: () => , 55 | // }); 56 | // 57 | // const LoadableHtmlToPdfMake = Loadable({ 58 | // loader: () => import('html-to-pdfmake'), 59 | // loading: () => , 60 | // }); 61 | 62 | const ReactQuill = LoadableReactQuill; 63 | // const htmlDocx = LoadableHtmlDocx; 64 | // const jsPDF = LoadableJsPDF; 65 | 66 | const StyledAppBar = styled(AppBar)(({ theme }) => ({ 67 | 68 | background: 'linear-gradient(135deg, #f5f5f5 0%, rgba(63,181,171,0.25) 100%)', 69 | boxShadow: '0 5px 15px rgba(0, 0, 0, 0.3)', 70 | })); 71 | 72 | const ChatRoomWrapper = styled(Box)(({ theme }) => ({ 73 | elevation:4, 74 | display: 'flex', 75 | flexDirection: 'column', 76 | flexGrow: 1, 77 | background: 'linear-gradient(135deg, #f5f5f5 0%, rgba(63,181,171,0.25) 100%)', 78 | padding: theme.spacing(2), 79 | overflowY: 'auto', 80 | marginRight: theme.spacing(1), 81 | width: '54vw', // Set a fixed width 82 | minWidth: '54vw', // Make sure the width doesn't shrink below the fixed width 83 | maxWidth: '54vw' 84 | 85 | })); 86 | const BackgroundWrapper = styled(Box)({ 87 | position: 'fixed', 88 | width: '100%', 89 | height: '100%', 90 | top: 0, 91 | left: 0, 92 | zIndex: -1, 93 | backgroundImage: `url('/bg-image.jpg')`, 94 | backgroundSize: 'cover', 95 | backgroundPosition: 'center', 96 | backgroundRepeat: 'no-repeat', 97 | animation: 'backgroundMove 30s infinite alternate', 98 | '@keyframes backgroundMove': { 99 | '0%': { 100 | backgroundPosition: 'left top', 101 | }, 102 | '50%': { 103 | backgroundPosition: 'right bottom', 104 | }, 105 | '100%': { 106 | backgroundPosition: 'left top', 107 | }, 108 | }, 109 | }); 110 | const NotesWrapper = styled(Box)(({ theme }) => ({ 111 | elevation:4, 112 | display: 'flex', 113 | flexDirection: 'column', 114 | flexGrow: 1, 115 | // color: 'rgb(29,45,35)', 116 | backgroundColor: '#f5f5f5', 117 | padding: theme.spacing(2), 118 | overflowY: 'auto', 119 | marginLeft: theme.spacing(1), 120 | width:'41vw', // Set a fixed width 121 | minWidth: '41vw', // Make sure the width doesn't shrink below the fixed width 122 | maxWidth: '41vw', 123 | borderRadius: '5px', // Add border radius 124 | boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)', // Add box shadow 125 | 126 | })); 127 | 128 | const quillToolbarOptions = [ 129 | [{ 'font': [] }, { 'size': ['small', false, 'large', 'huge'] }], 130 | ['bold', 'italic', 'underline', 'strike'], 131 | [{ 'color': [] }, { 'background': [] }], 132 | [{ 'script': 'sub' }, { 'script': 'super' }], 133 | [{ 'header': [1, 2, 3, 4, 5, 6, false] }], 134 | [{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'indent': '-1' }, { 'indent': '+1' }], 135 | ['direction', { 'align': [] }], 136 | ['link', 'image', 'video', 'formula'], 137 | ['clean'], 138 | ]; 139 | 140 | const StyledDivider = styled(Divider)(({ theme }) => ({ 141 | marginTop: theme.spacing(1), 142 | marginBottom: theme.spacing(1), 143 | })); 144 | 145 | 146 | interface TextProps { 147 | children: React.ReactNode; 148 | } 149 | 150 | const Text: React.FC = (props) => { 151 | return null; 152 | }; 153 | 154 | const StyledLink = styled('a')(({ theme }) => ({ 155 | paddingRight: "1vw", 156 | color: '#f5f5f5', 157 | textDecoration: 'underline', 158 | cursor: 'pointer', 159 | '&:hover': { 160 | textDecoration: 'none', 161 | }, 162 | })); 163 | 164 | const StyledDialogContent = styled(DialogContent)({ 165 | minWidth: 300, 166 | }); 167 | 168 | const MainPage: React.FC = () => { 169 | const router = useRouter(); 170 | pdfMake.vfs = pdfFonts.pdfMake.vfs; 171 | const [anchorEl, setAnchorEl] = useState(null); 172 | const [userMail, setUsermail] = useState(router.query.mail || ''); 173 | const [downanchorEl, setdownAnchorEl] = useState(null); 174 | const [openFeedbackDialog, setOpenFeedbackDialog] = useState(false); 175 | const [message, setMessage] = useState(''); 176 | 177 | const handleClick = (event: React.MouseEvent) => { 178 | setdownAnchorEl(event.currentTarget); 179 | }; 180 | 181 | const handleClose = () => { 182 | setdownAnchorEl(null); 183 | }; 184 | const handleMenuOpen = (event: React.MouseEvent) => { 185 | setAnchorEl(event.currentTarget); 186 | }; 187 | const notesContentRef = useRef(''); 188 | 189 | const handleMenuClose = () => { 190 | setAnchorEl(null); 191 | }; 192 | const handleMenuCloseLogout = () => { 193 | setAnchorEl(null); 194 | router.push("/"); 195 | }; 196 | const StyledReactQuill = styled(ReactQuill)(({ theme }) => ({ 197 | theme:"snow", 198 | border: '1px solid #e0e0e0', 199 | 200 | minHeight: 'calc(100% - 16px)', 201 | '& .ql-editor': { 202 | minHeight: 'calc(100% - 44px)', 203 | fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', 204 | 205 | }, 206 | })); 207 | const handleQuillChange = useCallback((content: string) => { 208 | notesContentRef.current = content; 209 | }, []); 210 | const downloadNotesAsWord = () => { 211 | const contentHtml = notesContentRef.current; 212 | const converted = htmlDocx.asBlob(contentHtml); 213 | saveAs(converted, 'notes.docx'); 214 | }; 215 | const downloadNotesAsPDF = () => { 216 | const contentHtml = notesContentRef.current; 217 | const pdfContent = htmlToPdfmake(contentHtml); 218 | const docDefinition: TDocumentDefinitions = { 219 | content: [ 220 | { 221 | stack: pdfContent, 222 | } as Content, 223 | ], 224 | defaultStyle: { 225 | font: 'Roboto', 226 | }, 227 | }; 228 | 229 | pdfMake.createPdf(docDefinition).download('notes.pdf'); 230 | }; 231 | 232 | const sendFeedback = () => { 233 | const feedback = { userMail, message }; 234 | fetch(`http://175.24.204.121:9091/feedbacks`, { 235 | method: "POST", 236 | mode: 'cors', 237 | headers: { "Content-Type": "application/json" }, 238 | body: JSON.stringify(feedback) 239 | 240 | }).then(res => res.json()) 241 | .then((res) => { 242 | if (res.code === '1') { 243 | alert("Your feedbacks was successfully sent"); 244 | setOpenFeedbackDialog(false); 245 | } 246 | else { 247 | alert("sent failed"); 248 | } 249 | 250 | }); 251 | }; 252 | 253 | return ( 254 | 255 | 256 | 257 | 268 | ChatNote 269 | 270 | 271 | GitHub 278 | setOpenFeedbackDialog(true)}> 279 | Share your feedbacks 280 | 281 | 282 | setOpenFeedbackDialog(false)} 285 | aria-labelledby="feedback-dialog-title" 286 | aria-describedby="feedback-dialog-description" 287 | > 288 | We value your feedbacks! 289 | 290 | 291 | Please enter your feedback below: 292 | 293 | setMessage(e.target.value)} 304 | /> 305 | 306 | 307 | 310 | 313 | 314 | 315 | 323 | 324 | 325 | 334 | Profile 335 | Settings 336 | Logout 337 | 338 | 339 | 340 | 341 | 342 | 343 | ChatRoom 344 | 345 | 346 | 347 | 348 | 349 | 350 | NoteBook 351 | 352 | 358 | 359 | 360 | 366 | { handleClose(); downloadNotesAsWord(); }}>Download as .docx 367 | { handleClose(); downloadNotesAsPDF(); }}>Download as .pdf 368 | 369 | 370 | 371 | 376 | 377 | 378 | 379 | ); 380 | } 381 | export default MainPage; -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/pages/signupPage.tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Image from 'next/image' 3 | import { Inter } from 'next/font/google' 4 | // import styles from '@/styles/Home.module.css' 5 | import React, {useEffect, useState} from 'react'; 6 | import { ReactNode } from 'react'; 7 | import { 8 | Container, 9 | TextField, 10 | Typography, 11 | Button, 12 | Link, 13 | Stack, 14 | Avatar, 15 | Paper, 16 | } from '@mui/material'; 17 | import { styled } from '@mui/system'; 18 | import { LockOutlined } from '@mui/icons-material'; 19 | import { useRouter } from 'next/router'; 20 | import dynamic from 'next/dynamic'; 21 | import { useSpring, animated } from 'react-spring'; 22 | 23 | 24 | import md5 from "js-md5"; 25 | 26 | import ParticlesBg, { Props } from 'particles-bg'; 27 | 28 | const DynamicParticlesBg = dynamic(() => import('particles-bg').then((mod) => mod.default), { 29 | ssr: false, 30 | }); 31 | 32 | import {BackgroundWrapper,WebsiteTitle,StyledContainer,StyledForm,StyledButton,StyledPaper} from './index' 33 | 34 | 35 | interface CopyrightProps { 36 | [key: string]: ReactNode; 37 | } 38 | 39 | function Copyright(props: CopyrightProps) { 40 | return ( 41 | 42 | {'Copyright © '} 43 | 44 | ChatNote powered by ChatGPT 45 | {' '} 46 | {new Date().getFullYear()} 47 | {'.'} 48 | 49 | ); 50 | } 51 | 52 | const LoginPage: React.FC = () => { 53 | const [name, setName] = useState(''); 54 | const [password, setPassword] = useState(''); 55 | const [email, setEmail] = useState(''); 56 | const [cell, setCell] = useState(''); 57 | const [nameerror, setnameError] = useState(""); 58 | const [passerror, setpassError] = useState(""); 59 | const [mailerror, setmailError] = useState(""); 60 | const [cellerror, setcellError] = useState(""); 61 | const [confirmedPassword, setConfirmedPassword] = useState(''); 62 | const [passwordsMatch, setPasswordsMatch] = useState(true); 63 | 64 | 65 | useEffect(() => { 66 | setPasswordsMatch(password === confirmedPassword); 67 | }, [password, confirmedPassword]); 68 | 69 | const handleSignup = (e: React.FormEvent) => { 70 | e.preventDefault(); 71 | const encryptedPassword = md5(password); 72 | const user={name,cell,email,encryptedPassword} 73 | 74 | console.log(user) 75 | fetch(`http://175.24.204.121:9091/register`,{ 76 | method:"POST", 77 | mode: 'cors', 78 | headers:{"Content-Type":"application/json"}, 79 | body:JSON.stringify(user) 80 | 81 | }).then(res=>res.json()) 82 | .then((res)=>{ 83 | if (res.code==='1'){alert("register successfully"); 84 | 85 | } 86 | else { 87 | alert("sign up failed") 88 | } 89 | 90 | }) 91 | 92 | 93 | } 94 | 95 | const formAnimation : any = useSpring({ 96 | from: { opacity: 0, transform: 'translate3d(0, -50px, 0)' }, 97 | to: { opacity: 1, transform: 'translate3d(0, 0, 0)' }, 98 | config: { duration: 1000 }, 99 | }) ; 100 | 101 | return ( 102 | 103 | 104 | 105 | 106 | ChatNote 107 | 108 | 109 | 110 | 111 | 112 | Sign up 113 | 114 | 115 | {setName(e.target.value)}} 128 | 129 | helperText={nameerror} 130 | /> 131 | 132 | setPassword(e.target.value)} 142 | 143 | helperText={passerror} 144 | /> 145 | setConfirmedPassword(e.target.value)} 157 | /> 158 | 159 | setEmail(e.target.value)} 170 | 171 | helperText={mailerror} 172 | // onBlur={(e)=>checkEmail(e)} 173 | 174 | /> 175 | setCell(e.target.value)} 186 | helperText={cellerror} 187 | /> 188 | 195 | Sign Up 196 | 197 | 198 | 199 | Back to Login 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | ); 210 | } 211 | 212 | export default LoginPage; 213 | 214 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/public/bg-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceDavy/ChatNote/e99d32ee0b4f2fab1fd216614fcc7dfaa7bc1357/Nextjs-Reactjs-frontend/public/bg-image.jpg -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceDavy/ChatNote/e99d32ee0b4f2fab1fd216614fcc7dfaa7bc1357/Nextjs-Reactjs-frontend/public/favicon.ico -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap'); 2 | 3 | /*:root {*/ 4 | /* --max-width: 1100px;*/ 5 | /* --border-radius: 12px;*/ 6 | /* --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',*/ 7 | /* 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',*/ 8 | /* 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;*/ 9 | 10 | /* --foreground-rgb: 0, 0, 0;*/ 11 | /* --background-start-rgb: 214, 219, 220;*/ 12 | /* --background-end-rgb: 255, 255, 255;*/ 13 | 14 | /* --primary-glow: conic-gradient(*/ 15 | /* from 180deg at 50% 50%,*/ 16 | /* #16abff33 0deg,*/ 17 | /* #0885ff33 55deg,*/ 18 | /* #54d6ff33 120deg,*/ 19 | /* #0071ff33 160deg,*/ 20 | /* transparent 360deg*/ 21 | /* );*/ 22 | /* --secondary-glow: radial-gradient(*/ 23 | /* rgba(255, 255, 255, 1),*/ 24 | /* rgba(255, 255, 255, 0)*/ 25 | /* );*/ 26 | 27 | /* --tile-start-rgb: 239, 245, 249;*/ 28 | /* --tile-end-rgb: 228, 232, 233;*/ 29 | /* --tile-border: conic-gradient(*/ 30 | /* #00000080,*/ 31 | /* #00000040,*/ 32 | /* #00000030,*/ 33 | /* #00000020,*/ 34 | /* #00000010,*/ 35 | /* #00000010,*/ 36 | /* #00000080*/ 37 | /* );*/ 38 | 39 | /* --callout-rgb: 238, 240, 241;*/ 40 | /* --callout-border-rgb: 172, 175, 176;*/ 41 | /* --card-rgb: 180, 185, 188;*/ 42 | /* --card-border-rgb: 131, 134, 135;*/ 43 | /*}*/ 44 | 45 | /*@media (prefers-color-scheme: dark) {*/ 46 | /* :root {*/ 47 | /* --foreground-rgb: 255, 255, 255;*/ 48 | /* --background-start-rgb: 0, 0, 0;*/ 49 | /* --background-end-rgb: 0, 0, 0;*/ 50 | 51 | /* --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));*/ 52 | /* --secondary-glow: linear-gradient(*/ 53 | /* to bottom right,*/ 54 | /* rgba(1, 65, 255, 0),*/ 55 | /* rgba(1, 65, 255, 0),*/ 56 | /* rgba(1, 65, 255, 0.3)*/ 57 | /* );*/ 58 | 59 | /* --tile-start-rgb: 2, 13, 46;*/ 60 | /* --tile-end-rgb: 2, 5, 19;*/ 61 | /* --tile-border: conic-gradient(*/ 62 | /* #ffffff80,*/ 63 | /* #ffffff40,*/ 64 | /* #ffffff30,*/ 65 | /* #ffffff20,*/ 66 | /* #ffffff10,*/ 67 | /* #ffffff10,*/ 68 | /* #ffffff80*/ 69 | /* );*/ 70 | 71 | /* --callout-rgb: 20, 20, 20;*/ 72 | /* --callout-border-rgb: 108, 108, 108;*/ 73 | /* --card-rgb: 100, 100, 100;*/ 74 | /* --card-border-rgb: 200, 200, 200;*/ 75 | /* }*/ 76 | /*}*/ 77 | 78 | /** {*/ 79 | /* box-sizing: border-box;*/ 80 | /* padding: 0;*/ 81 | /* margin: 0;*/ 82 | /*}*/ 83 | 84 | /*html,*/ 85 | /*body {*/ 86 | /* max-width: 100vw;*/ 87 | /* overflow-x: hidden;*/ 88 | /*}*/ 89 | 90 | /*body {*/ 91 | /* color: rgb(var(--foreground-rgb));*/ 92 | /* background: linear-gradient(*/ 93 | /* to bottom,*/ 94 | /* transparent,*/ 95 | /* rgb(var(--background-end-rgb))*/ 96 | /* )*/ 97 | /* rgb(var(--background-start-rgb));*/ 98 | /*}*/ 99 | 100 | /*a {*/ 101 | /* color: inherit;*/ 102 | /* text-decoration: none;*/ 103 | /*}*/ 104 | 105 | /*@media (prefers-color-scheme: dark) {*/ 106 | /* html {*/ 107 | /* color-scheme: dark;*/ 108 | /* }*/ 109 | /*}*/ 110 | 111 | @font-face { 112 | font-family: 'Great Vibes, cursive'; 113 | 114 | font-weight: normal; 115 | font-display: swap; 116 | } 117 | .App { 118 | background-color: #f2f2f2; 119 | padding: 20px; 120 | border-radius: 30px; 121 | box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); 122 | display: flex; 123 | flex-direction: column; 124 | align-items: center; 125 | justify-content: center; 126 | } 127 | .chat-container { 128 | display: flex; 129 | justify-content: center; 130 | 131 | height: 100vh; 132 | width: 100vh; 133 | margin-top: 20px; 134 | } 135 | .sophisticated { 136 | font-family: "Times New Roman", serif; 137 | letter-spacing: 2px; 138 | color: #555; 139 | border-collapse: separate; 140 | background-image: linear-gradient(to bottom, #f5f5f5, #fff); 141 | font-size: 25px; 142 | font-weight: bold; 143 | line-height: 1.5; 144 | } 145 | .falling-words-container { 146 | position: relative; 147 | overflow: hidden; 148 | height: 100vh; 149 | } 150 | 151 | .falling-word { 152 | position: absolute; 153 | top: 0; 154 | left: 50%; 155 | transform: translateX(-50%); 156 | animation: fall 10s linear infinite; 157 | font-size: 24px; 158 | color: #fff; 159 | text-shadow: 0 0 5px #000; 160 | } 161 | 162 | @keyframes fall { 163 | 0% { 164 | transform: translateY(-100%); 165 | } 166 | 100% { 167 | transform: translateY(100vh); 168 | } 169 | } 170 | .logo:hover { 171 | filter: drop-shadow(0 0 2em #646cffaa); 172 | } 173 | .logo.react:hover { 174 | filter: drop-shadow(0 0 2em #61dafbaa); 175 | } 176 | 177 | @keyframes logo-spin { 178 | from { 179 | transform: rotate(0deg); 180 | } 181 | to { 182 | transform: rotate(360deg); 183 | } 184 | } 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "compilerOptions": { 4 | "typeRoots": ["./node_modules/@types", "./types"], 5 | "target": "es5", 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "allowJs": true, 8 | "skipLibCheck": true, 9 | "strict": false, 10 | "ignoreDeprecations": "5.0", 11 | "noImplicitUseStrict": true, 12 | "noStrictGenericChecks": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noEmit": true, 15 | "esModuleInterop": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve", 21 | "incremental": true, 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /Nextjs-Reactjs-frontend/types/html-docx-js.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'html-docx-js/dist/html-docx' { 2 | export default class HtmlDocx { 3 | static asBlob(content: string): Blob; 4 | } 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI-Playground :rocket: 2 | 3 | Welcome to the `AI-Playground`, an interactive web application that leverages the power of OpenAI's API, including `ChatGPT`, `DALL-E`, and `Text-Davinci`. This project is built using `Next.js` and `TypeScript` for the front end, and `Spring Boot` with `MongoDB` for the back end. `RabbitMQ`, `Redis`, `Clash` and other tech stacks are applied for flexibility. 4 | 5 | 6 |

7 | your_image_description 8 | your_image_description 9 | 10 |

11 | 12 | ## View Live Demo(deprecated): 13 | http://chatnote.live 14 | 15 | ## Table of Contents 16 | 17 | - [Features](#features) 18 | - [Prerequisites](#prerequisites) 19 | - [Installation](#installation) 20 | - [Usage](#usage) 21 | - [Contribution](#contribution) 22 | - [License](#license) 23 | 24 | ## Features 25 | 26 | - :speech_balloon: **ChatGPT**: Experience the AI-powered language model that can generate human-like text based on given input. 27 | - :art: **DALL-E**: Generate creative and unique images from textual descriptions using the DALL-E model. 28 | - :book: **Text-Davinci**: Use the power of Text-Davinci for complex tasks like content generation, semantic search, and more. 29 | - :zap: **Next.js & TypeScript**: A blazing-fast, modern front-end built using Next.js and TypeScript for excellent user experience. 30 | - :rocket: **Spring Boot & MongoDB**: A robust and scalable back end with Spring Boot and MongoDB for efficient data storage and retrieval. 31 | 32 | ## Prerequisites 33 | 34 | Before you can run the project, make sure you have the following installed: 35 | 36 | - Node.js (v14.x or higher) 37 | - npm (v6.x or higher) 38 | - Java (v11 or higher) 39 | - MongoDB (v4.4 or higher) 40 | 41 | Additionally, you'll need an OpenAI API key for using the ChatGPT, DALL-E, and Text-Davinci services. 42 | 43 | ## Installation 44 | 45 | Follow these steps to set up the project on your local machine: 46 | 47 | 1. Clone the repository: 48 | 49 | git clone https://github.com/OpenSourceDavy/ChatNote.git 50 | cd ai-playground 51 | 52 | 2. Install the front-end dependencies: 53 | 54 | cd frontend 55 | npm install 56 | 57 | 3. Set up the environment variables for the front end: 58 | 59 | cp .env.example .env.local 60 | 61 | 62 | Now, open the .env.local file and fill in your OpenAI API key: 63 | 64 | NEXT_PUBLIC_OPENAI_API_KEY=your_openai_api_key_here 65 | 66 | 67 | Install the back-end dependencies: 68 | 69 | cd ../backend 70 | ./mvnw clean install 71 | 72 | Set up the environment variables for the back end: 73 | Create a new file called application.properties inside the src/main/resources folder and add the following content: 74 | 75 | spring.data.mongodb.uri=mongodb://localhost:27017/aiplayground 76 | openai.api.key=your_openai_api_key_here 77 | 78 | ## Usage 79 | To run the project, follow these steps: 80 | 81 | Start the back end: 82 | 83 | cd backend 84 | ./mvnw spring-boot:run 85 | 86 | Start the front end: 87 | 88 | First, run the development server: 89 | 90 | ```bash 91 | npm run dev 92 | # or 93 | yarn dev 94 | # or 95 | pnpm dev 96 | ``` 97 | 98 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 99 | 100 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 101 | 102 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 103 | 104 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 105 | 106 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 107 | 108 | ## Learn More 109 | 110 | To learn more about Next.js, take a look at the following resources: 111 | 112 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 113 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 114 | 115 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 116 | 117 | ## Deploy on Vercel 118 | 119 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 120 | 121 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 122 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/mongdb.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.5.2 9 | 10 | 11 | com.fish 12 | mongodb 13 | 0.0.1-SNAPSHOT 14 | mongodb 15 | mongodb project for Spring Boot 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-data-mongodb 34 | 35 | 36 | org.projectlombok 37 | lombok 38 | 39 | 40 | junit 41 | junit 42 | test 43 | 44 | 45 | 46 | cn.hutool 47 | hutool-all 48 | 5.7.16 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | com.github.shalousun 61 | smart-doc-maven-plugin 62 | 2.4.8 63 | 64 | 65 | ./src/main/resources/smart-doc.json 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | com.alibaba:fastjson 74 | 75 | 76 | 77 | 78 | 79 | 80 | com.alibaba:fastjson 81 | 82 | 83 | 84 | 85 | 86 | compile 87 | 88 | 89 | html 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/MongodbApplication.java: -------------------------------------------------------------------------------- 1 | package com.mongdb; 2 | import org.springframework.boot.SpringApplication; 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | /** 6 | * @author Dc 7 | */ 8 | @SpringBootApplication 9 | public class MongodbApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(MongodbApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/common/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.common; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class CorsConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addCorsMappings(CorsRegistry registry) { 12 | registry.addMapping("/**") 13 | .allowedOrigins("*") 14 | .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") 15 | .allowedHeaders("*") 16 | .maxAge(24 * 60 * 60); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/common/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.common; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | 6 | import com.mongdb.model.UserModel; 7 | 8 | import java.util.Date; 9 | 10 | public class JwtUtil { 11 | 12 | private static final String SECRET = "JWT_SECRET"; 13 | private static final int EXPIRATION_TIME = 864000000; // 10 days 14 | 15 | public static String createToken(UserModel user) { 16 | return JWT.create() 17 | .withSubject(user.getEmail()) 18 | .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) 19 | .sign(Algorithm.HMAC512(SECRET)); 20 | } 21 | 22 | public static String validateToken(String token) { 23 | return JWT.require(Algorithm.HMAC512(SECRET)) 24 | .build() 25 | .verify(token) 26 | .getSubject(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/common/RabbitMQConfig.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.common; 2 | 3 | import org.springframework.amqp.core.Queue; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class RabbitMQConfig { 9 | 10 | public static final String EMAIL_QUEUE = "email_queue"; 11 | 12 | @Bean 13 | public Queue emailQueue() { 14 | return new Queue(EMAIL_QUEUE, false); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/common/Result.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.common; 2 | 3 | public class Result { 4 | private String code; 5 | private String msg; 6 | private T data; 7 | 8 | public String getCode() { 9 | return code; 10 | } 11 | 12 | public void setCode(String code) { 13 | this.code = code; 14 | } 15 | 16 | public String getMsg() { 17 | return msg; 18 | } 19 | 20 | public void setMsg(String msg) { 21 | this.msg = msg; 22 | } 23 | 24 | public T getData() { 25 | return data; 26 | } 27 | 28 | public void setData(T data) { 29 | this.data = data; 30 | } 31 | 32 | public Result() { 33 | } 34 | 35 | public Result(T data) { 36 | this.data = data; 37 | } 38 | 39 | public static Result success() { 40 | Result result = new Result<>(); 41 | result.setCode("1"); 42 | result.setMsg("success"); 43 | return result; 44 | } 45 | 46 | public static Result success(T data) { 47 | Result result = new Result<>(data); 48 | result.setCode("1"); 49 | result.setMsg("success"); 50 | return result; 51 | } 52 | 53 | public static Result error(String code, String msg) { 54 | Result result = new Result(); 55 | result.setCode(code); 56 | result.setMsg(msg); 57 | return result; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/common/SendGridConfig.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.common; 2 | 3 | import com.sendgrid.SendGrid; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class SendGridConfig { 10 | 11 | @Value("${sendgrid.api-key}") 12 | private String apiKey; 13 | 14 | @Bean 15 | public SendGrid sendGrid() { 16 | return new SendGrid(apiKey); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/common/WebClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.common; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.http.client.reactive.ReactorClientHttpConnector; 8 | import org.springframework.web.reactive.function.client.WebClient; 9 | import reactor.netty.http.client.HttpClient; 10 | import reactor.netty.transport.ProxyProvider; 11 | 12 | import java.time.Duration; 13 | 14 | @Configuration 15 | public class WebClientConfig { 16 | private static final Logger LOGGER = LoggerFactory.getLogger(WebClientConfig.class); 17 | 18 | @Bean 19 | public WebClient webClient() { 20 | LOGGER.info("Configuring WebClient with proxy settings..."); 21 | 22 | HttpClient httpClient = HttpClient.create() 23 | .tcpConfiguration(tcpClient -> 24 | tcpClient.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP) 25 | .host("127.0.0.1") 26 | .port(7890))) 27 | .responseTimeout(Duration.ofSeconds(30)) 28 | ; 29 | 30 | return WebClient.builder() 31 | .clientConnector(new ReactorClientHttpConnector(httpClient)) 32 | .build(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/controller/ApiController.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.controller; 2 | 3 | import com.mongdb.model.ChatCompletionRequest; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.springframework.http.HttpEntity; 6 | import org.springframework.http.HttpHeaders; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.web.reactive.function.client.WebClient; 15 | import reactor.core.publisher.Mono; 16 | 17 | 18 | @RestController 19 | public class ApiController { 20 | @Autowired 21 | private WebClient webClient; 22 | 23 | @PostMapping("/openai") 24 | public Mono getOpenAIResponse(@RequestBody ChatCompletionRequest chatCompletionRequest) { 25 | String url = "https://api.openai.com/v1/chat/completions"; 26 | String apiKey = ""; // Replace with your OpenAI API key 27 | 28 | HttpHeaders headers = new HttpHeaders(); 29 | headers.set("Content-Type", "application/json"); 30 | headers.set("Authorization", "Bearer " + apiKey); 31 | 32 | ObjectMapper objectMapper = new ObjectMapper(); 33 | String requestBody = ""; 34 | 35 | try { 36 | requestBody = objectMapper.writeValueAsString(chatCompletionRequest); 37 | System.out.println("Recieve from frontend: " + requestBody); 38 | } catch (Exception e) { 39 | System.err.println("Error converting request body to JSON: " + e.getMessage()); 40 | return Mono.just("Error converting request body to JSON: " + e.getMessage()); 41 | } 42 | 43 | 44 | 45 | return webClient.post() 46 | .uri(url) 47 | .header("Content-Type", "application/json") 48 | .header("Authorization", "Bearer " + apiKey) 49 | .bodyValue(requestBody) 50 | 51 | .retrieve() 52 | 53 | .bodyToMono(String.class) 54 | 55 | .doOnSubscribe(subscription -> { 56 | System.out.println("Request sent to OpenAI API"); 57 | }) 58 | .doOnNext(responseBody -> { 59 | System.out.println("API Response: " + responseBody); 60 | }) 61 | .doOnSuccess(responseEntity -> { 62 | System.out.println("API Response successfully returned to frontend"); 63 | }) 64 | .onErrorResume(e -> { 65 | System.err.println("Error: " + e.getMessage()); 66 | return Mono.just("Error: " + e.getMessage()); 67 | }); 68 | } 69 | @PostMapping("/openai4") 70 | public Mono getOpenAI4Response(@RequestBody ChatCompletionRequest chatCompletionRequest) { 71 | String url = "https://api.openai.com/v1/chat/completions"; 72 | String apiKey = ""; // Replace with your OpenAI API key 73 | 74 | HttpHeaders headers = new HttpHeaders(); 75 | headers.set("Content-Type", "application/json"); 76 | headers.set("Authorization", "Bearer " + apiKey); 77 | 78 | ObjectMapper objectMapper = new ObjectMapper(); 79 | String requestBody = ""; 80 | 81 | try { 82 | requestBody = objectMapper.writeValueAsString(chatCompletionRequest); 83 | System.out.println("Recieve from frontend: " + requestBody); 84 | } catch (Exception e) { 85 | System.err.println("Error converting request body to JSON: " + e.getMessage()); 86 | return Mono.just("Error converting request body to JSON: " + e.getMessage()); 87 | } 88 | 89 | 90 | 91 | return webClient.post() 92 | .uri(url) 93 | .header("Content-Type", "application/json") 94 | .header("Authorization", "Bearer " + apiKey) 95 | .bodyValue(requestBody) 96 | 97 | .retrieve() 98 | 99 | .bodyToMono(String.class) 100 | 101 | .doOnSubscribe(subscription -> { 102 | System.out.println("Request sent to OpenAI API"); 103 | }) 104 | .doOnNext(responseBody -> { 105 | System.out.println("API Response: " + responseBody); 106 | }) 107 | .doOnSuccess(responseEntity -> { 108 | System.out.println("API Response successfully returned to frontend"); 109 | }) 110 | .onErrorResume(e -> { 111 | System.err.println("Error: " + e.getMessage()); 112 | return Mono.just("Error: " + e.getMessage()); 113 | }); 114 | } 115 | } -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/controller/MessageController.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.controller; 2 | 3 | import com.mongdb.common.Result; 4 | import com.mongdb.dao.MessageDao; 5 | import com.mongdb.model.MessageModel; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | public class MessageController { 14 | @Autowired 15 | private MessageDao messageDao; 16 | 17 | @PostMapping(value="/feedbacks") 18 | public Result register(@RequestBody MessageModel messageModel) throws Exception { 19 | System.out.println("Feedbacks method called with userModel: " + messageModel.getUserMail()); 20 | 21 | messageDao.saveTest(messageModel); 22 | return Result.success('1'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.controller; 2 | 3 | 4 | import com.mongdb.common.Result; 5 | import com.mongdb.dao.EmailService; 6 | import com.mongdb.dao.UserDao; 7 | import com.mongdb.model.UserModel; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @RestController 15 | public class UserController { 16 | @Autowired 17 | private UserDao userDao; 18 | private EmailService emailService; 19 | 20 | @PostMapping(value="/register") 21 | public Result register(@RequestBody UserModel userModel) throws Exception { 22 | System.out.println("Register method called with userModel: " + userModel); 23 | 24 | UserModel user = userDao.findTestByEmail(userModel.getEmail()); 25 | if(user!=null){ 26 | return Result.error("0","this email already exist"); 27 | } 28 | userDao.saveTest(userModel); 29 | return Result.success('1'); 30 | } 31 | 32 | 33 | @PostMapping(value="/login") 34 | public Result login(@RequestBody UserModel userModel){ 35 | 36 | 37 | 38 | UserModel user = userDao.findTestByEmailAndPassWord(userModel.getEmail(),userModel.getEncryptedPassword()); 39 | System.out.println("user is "+user); 40 | if(user == null){ 41 | return Result.error("-1","wrong mail or password"); 42 | } 43 | return Result.success(user); 44 | } 45 | 46 | @PostMapping("/subscribe") 47 | public Result subscribeUser(@RequestBody UserModel userModel) { 48 | // Save the user to the database 49 | userModel.setSubscribed(true); 50 | userDao.saveTest(userModel); 51 | 52 | // Send a welcome email to the user 53 | String subject = "Welcome to ChatNote!"; 54 | String text = "Hello " + userModel.getEmail() + ",\n\n" + 55 | "Thank you for subscribing to our website. We're excited to have you on board!\n\n" + 56 | "Best Regards,\n" + 57 | "The Team"; 58 | boolean mailSent = emailService.sendSimpleEmail(userModel.getEmail(), subject, text); 59 | if (mailSent==true){ 60 | return Result.success('1'); 61 | }else{ 62 | return Result.error("0","cannot send mail"); 63 | } 64 | } 65 | 66 | @GetMapping(value="/test3") 67 | public void updateTest(){ 68 | UserModel user=new UserModel(); 69 | user.setName("hi"); 70 | userDao.updateTest(user); 71 | } 72 | 73 | @GetMapping(value="/test4") 74 | public void deleteTestById(){ 75 | userDao.deleteTestById(1); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/dao/EmailService.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.dao; 2 | 3 | import com.mongdb.common.RabbitMQConfig; 4 | import com.mongdb.model.EmailMessage; 5 | import org.springframework.amqp.rabbit.connection.CorrelationData; 6 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 7 | import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | public class EmailService { 13 | 14 | @Autowired 15 | private RabbitTemplate rabbitTemplate; 16 | 17 | 18 | public EmailService(RabbitTemplate rabbitTemplate) { 19 | this.rabbitTemplate = rabbitTemplate; 20 | // 设置确认回调 21 | this.rabbitTemplate.setConfirmCallback(confirmCallback()); 22 | } 23 | public boolean sendSimpleEmail(String to, String subject, String text) { 24 | boolean[] isSent = {false}; // 用于保存发送状态的变量 25 | try { 26 | EmailMessage emailMessage = new EmailMessage(to, subject, text); 27 | CorrelationData correlationData = new CorrelationData(); 28 | rabbitTemplate.convertAndSend(RabbitMQConfig.EMAIL_QUEUE, emailMessage, correlationData); 29 | synchronized (isSent) { 30 | isSent.wait(5000); // 等待回调设置isSent的值 31 | } 32 | } catch (InterruptedException e) { 33 | e.printStackTrace(); 34 | } 35 | return isSent[0]; // 返回发送状态 36 | } 37 | 38 | private ConfirmCallback confirmCallback() { 39 | return (correlationData, ack, cause) -> { 40 | boolean[] isSent = {false}; 41 | if (ack) { 42 | isSent[0] = true; 43 | } else { 44 | System.out.println("Message failed: " + cause); 45 | isSent[0] = false; 46 | } 47 | synchronized (isSent) { 48 | isSent.notify(); // 唤醒等待线程 49 | } 50 | }; 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/dao/MessageDao.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.dao; 2 | 3 | import com.mongdb.model.MessageModel; 4 | import com.mongdb.model.UserModel; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.data.mongodb.core.MongoTemplate; 7 | import org.springframework.data.mongodb.core.query.Criteria; 8 | import org.springframework.data.mongodb.core.query.Query; 9 | import org.springframework.data.mongodb.core.query.Update; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | public class MessageDao { 14 | 15 | 16 | @Autowired 17 | private MongoTemplate mongoTemplate; 18 | /** 19 | * 创建对象 20 | */ 21 | public void saveTest(MessageModel messageModel) { 22 | mongoTemplate.save(messageModel); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/dao/RedisService.java: -------------------------------------------------------------------------------- 1 | import org.springframework.data.redis.core.RedisTemplate; 2 | import org.springframework.data.redis.core.StringRedisTemplate; 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; 7 | import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | @Service 12 | public class RedisService { 13 | private final RedisTemplate redisTemplate; 14 | 15 | public RedisService(RedisTemplate redisTemplate) { 16 | this.redisTemplate = redisTemplate; 17 | // 设置 RedisTemplate 的序列化器,以支持复杂类型的序列化 18 | Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); 19 | ObjectMapper objectMapper = new ObjectMapper(); 20 | PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().allowIfBaseType(Object.class).build(); 21 | objectMapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL); 22 | serializer.setObjectMapper(objectMapper); 23 | redisTemplate.setValueSerializer(serializer); 24 | } 25 | 26 | public void setValue(String key, Object value) { 27 | redisTemplate.opsForValue().set(key, value); 28 | } 29 | 30 | public void setValue(String key, Object value, long timeout, TimeUnit unit) { 31 | redisTemplate.opsForValue().set(key, value, timeout, unit); 32 | } 33 | 34 | public Object getValue(String key) { 35 | return redisTemplate.opsForValue().get(key); 36 | } 37 | 38 | public void deleteValue(String key) { 39 | redisTemplate.delete(key); 40 | } 41 | } -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.dao; 2 | 3 | import com.mongdb.model.UserModel; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.data.mongodb.core.MongoTemplate; 6 | import org.springframework.data.mongodb.core.query.Criteria; 7 | import org.springframework.data.mongodb.core.query.Query; 8 | import org.springframework.data.mongodb.core.query.Update; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class UserDao { 13 | @Autowired 14 | private MongoTemplate mongoTemplate; 15 | 16 | /** 17 | * 创建对象 18 | */ 19 | public void saveTest(UserModel user) { 20 | mongoTemplate.save(user); 21 | } 22 | 23 | /** 24 | * 根据用户名和密码查询对象 25 | * @return 26 | */ 27 | public UserModel findTestByNameAndPassWord(String name, String pwd) { 28 | Query query=new Query(Criteria.where("name").is(name)).addCriteria(Criteria.where("encryptedPassword").is(pwd)); 29 | UserModel mgt = mongoTemplate.findOne(query , UserModel.class); 30 | return mgt; 31 | } 32 | public UserModel findTestByEmailAndPassWord(String email, String pwd) { 33 | Query query=new Query(Criteria.where("email").is(email)).addCriteria(Criteria.where("encryptedPassword").is(pwd)); 34 | UserModel mgt = mongoTemplate.findOne(query , UserModel.class); 35 | return mgt; 36 | } 37 | 38 | /** 39 | * 根据用户名查询对象 40 | * @return 41 | */ 42 | public UserModel findTestByName(String name) { 43 | Query query=new Query(Criteria.where("name").is(name)); 44 | UserModel mgt = mongoTemplate.findOne(query , UserModel.class); 45 | return mgt; 46 | } 47 | 48 | /** 49 | * 根据邮箱查询对象 50 | * @return 51 | */ 52 | public UserModel findTestByEmail(String email) { 53 | Query query=new Query(Criteria.where("email").is(email)); 54 | UserModel mgt = mongoTemplate.findOne(query , UserModel.class); 55 | return mgt; 56 | } 57 | 58 | /** 59 | * 更新对象 60 | */ 61 | public void updateTest(UserModel test) { 62 | Query query=new Query(Criteria.where("email").is(test.getEmail())); 63 | Update update= new Update().set("encryptedPassword", test.getEncryptedPassword()).set("name", test.getName()); 64 | //更新查询返回结果集的第一条 65 | mongoTemplate.updateFirst(query,update, UserModel.class); 66 | //更新查询返回结果集的所有 67 | // mongoTemplate.updateMulti(query,update,TestEntity.class); 68 | } 69 | 70 | /** 71 | * 删除对象 72 | * @param id 73 | */ 74 | public void deleteTestById(Integer id) { 75 | Query query=new Query(Criteria.where("id").is(id)); 76 | mongoTemplate.remove(query, UserModel.class); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/listeners/EmailListener.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.listeners; 2 | 3 | import com.mongdb.common.RabbitMQConfig; 4 | import com.mongdb.model.EmailMessage; 5 | import com.sendgrid.*; 6 | import com.sendgrid.helpers.mail.Mail; 7 | import com.sendgrid.helpers.mail.objects.Content; 8 | import com.sendgrid.helpers.mail.objects.Email; 9 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.io.IOException; 14 | 15 | @Component 16 | public class EmailListener { 17 | 18 | @Autowired 19 | private SendGrid sendGrid; 20 | //rabbit listener auto send ack if message is consumed 21 | @RabbitListener(queues = RabbitMQConfig.EMAIL_QUEUE) 22 | public void sendEmail(EmailMessage emailMessage) { 23 | Email from = new Email("davy3232323@gmail.com"); 24 | Email recipient = new Email(emailMessage.getTo()); 25 | Content content = new Content("text/plain", emailMessage.getText()); 26 | Mail mail = new Mail(from, emailMessage.getSubject(), recipient, content); 27 | 28 | Request request = new Request(); 29 | try { 30 | request.setMethod(Method.POST); 31 | request.setEndpoint("mail/send"); 32 | request.setBody(mail.build()); 33 | Response response = sendGrid.api(request); 34 | } catch (IOException ex) { 35 | // Handle exception 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/model/ApiModel.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ApiModel { 7 | private String role; 8 | private String content; 9 | // private String sentTime; 10 | // private String sender; 11 | // private String direction; 12 | } 13 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/model/ChatCompletionRequest.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.model; 2 | 3 | 4 | import java.util.List; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class ChatCompletionRequest { 9 | 10 | private List messages; 11 | private String model; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/model/EmailMessage.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.model; 2 | 3 | import java.io.Serializable; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class EmailMessage implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private String to; 13 | private String subject; 14 | private String text; 15 | 16 | public EmailMessage(String to, String subject, String text) { 17 | this.to = to; 18 | this.subject = subject; 19 | this.text = text; 20 | } 21 | 22 | // Constructor, getters, and setters 23 | } 24 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/model/MessageModel.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class MessageModel { 7 | private String userMail; 8 | private String message; 9 | } 10 | -------------------------------------------------------------------------------- /Springboot-MongoDB-backend/src/main/java/com/mongdb/model/UserModel.java: -------------------------------------------------------------------------------- 1 | package com.mongdb.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class UserModel { 7 | private String name; 8 | private String cell; 9 | private String email; 10 | private String encryptedPassword; 11 | private boolean isSubscribed; 12 | } 13 | --------------------------------------------------------------------------------