tekville commited on
Commit
ff72db3
ยท
0 Parent(s):

Initial commit

Browse files
.dockerignore ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Git ๊ด€๋ จ ํŒŒ์ผ
2
+ .git
3
+ .gitignore
4
+
5
+ # ๊ฐ€์ƒํ™˜๊ฒฝ
6
+ .venv
7
+ __pycache__
8
+
9
+ # ์บ์‹œ ํŒŒ์ผ
10
+ *.pyc
11
+ *.pyo
12
+ *.log
13
+ *.tmp
14
+
15
+ # ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ
16
+ dist/
17
+ build/
18
+ rag/
19
+ *.egg-info/
20
+
21
+ # ์‹œ์Šคํ…œ ํŒŒ์ผ
22
+ .DS_Store
23
+ Thumbs.db
24
+
25
+ # IDE ์„ค์ • ํŒŒ์ผ
26
+ .idea/
27
+ .vscode/
28
+ *.code-workspace
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ env/
12
+ venv/
13
+ ENV/
14
+ env.bak/
15
+ venv.bak/
16
+ *.egg
17
+ *.egg-info/
18
+ dist/
19
+ build/
20
+ *.wheel
21
+ .venv/
22
+ fastapi/
23
+ rag/
24
+
25
+ # PyInstaller
26
+ # Usually these files are written by a python script from a template
27
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
28
+ *.manifest
29
+ *.spec
30
+
31
+ # Installer logs
32
+ pip-log.txt
33
+ pip-delete-this-directory.txt
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .nox/
39
+ .coverage
40
+ *.coverage
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+ *.cover
45
+ *.py,cover
46
+ .hypothesis/
47
+ pytest_cache/
48
+ cover/
49
+
50
+ # Jupyter Notebook
51
+ .ipynb_checkpoints
52
+
53
+ # PyCharm
54
+ .idea/
55
+
56
+ # VSCode
57
+ .vscode/
58
+ *.code-workspace
59
+
60
+ # MyPy
61
+ .mypy_cache/
62
+
63
+ # Pyre
64
+ .pyre/
65
+
66
+ # Pytype
67
+ .pytype/
68
+
69
+ # Celery beat schedule file
70
+ celerybeat-schedule
71
+
72
+ # Django stuff:
73
+ *.log
74
+ local_settings.py
75
+ db.sqlite3
76
+ db.sqlite3-journal
77
+
78
+ # Flask stuff:
79
+ instance/
80
+ .webassets-cache
81
+
82
+ # Scrapy stuff:
83
+ .scrapy
84
+
85
+ # Sphinx documentation
86
+ docs/_build/
87
+
88
+ # PyBuilder
89
+ target/
90
+
91
+ # dotenv
92
+ .env
93
+ *.env
94
+
95
+ # IDEs and editors
96
+ *.swp
97
+ *~
98
+ .vimrc
99
+ .history
100
+ .idea/
101
+ *.sublime-project
102
+ *.sublime-workspace
103
+
104
+ # macOS
105
+ .DS_Store
106
+
107
+ # Windows
108
+ Thumbs.db
109
+ desktop.ini
110
+
111
+ # Logs
112
+ *.log
113
+ *.out
114
+ *.err
Dockerfile ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Build Stage
2
+ FROM python:3.11-slim as builder
3
+
4
+ # ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
5
+ ENV HOME=/home/user \
6
+ PATH=/home/user/.local/bin:$PATH
7
+
8
+ # ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ์„ค์ •
9
+ RUN useradd -m -d $HOME -s /bin/bash user
10
+
11
+ # ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ ์„ค์ •
12
+ WORKDIR $HOME/app
13
+
14
+ # ์‹œ์Šคํ…œ ํŒจํ‚ค์ง€ ์—…๋ฐ์ดํŠธ ๋ฐ ํ•„์ˆ˜ ํŒจํ‚ค์ง€ ์„ค์น˜
15
+ RUN apt-get update && apt-get install -y \
16
+ build-essential \
17
+ libmariadb-dev \
18
+ git \
19
+ && rm -rf /var/lib/apt/lists/*
20
+
21
+ # Python ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ
22
+ RUN pip install --upgrade pip
23
+
24
+ # ์˜์กด์„ฑ ํŒŒ์ผ ๋ณต์‚ฌ
25
+ COPY --chown=user:user requirements.txt ./
26
+
27
+ # Python ํŒจํ‚ค์ง€ ์„ค์น˜
28
+ RUN pip install --no-cache-dir -r requirements.txt
29
+
30
+ # Runtime Stage
31
+ FROM python:3.11-slim
32
+
33
+ # ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
34
+ ENV HOME=/home/user \
35
+ PATH=/home/user/.local/bin:$PATH
36
+
37
+ # ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ์„ค์ •
38
+ RUN useradd -m -d $HOME -s /bin/bash user
39
+
40
+ # ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ ์„ค์ •
41
+ WORKDIR $HOME/app
42
+
43
+ # ํ•„์š”ํ•œ ์‹œ์Šคํ…œ ํŒจํ‚ค์ง€ ์„ค์น˜
44
+ RUN apt-get update && apt-get install -y \
45
+ libmariadb-dev \
46
+ wget \
47
+ git \
48
+ && rm -rf /var/lib/apt/lists/*
49
+
50
+ # Build Stage์—์„œ ์„ค์น˜๋œ Python ํŒจํ‚ค์ง€ ๋ณต์‚ฌ
51
+ COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
52
+ COPY --from=builder /usr/local/bin /usr/local/bin
53
+
54
+ # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ๋ณต์‚ฌ
55
+ COPY --chown=user:user . .
56
+
57
+ # ํฌํŠธ ๋…ธ์ถœ (FastAPI ๊ธฐ๋ณธ ํฌํŠธ: 8000)
58
+ EXPOSE 8000
59
+
60
+ # ์—…๋กœ๋“œ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ์„ค์ •
61
+ RUN mkdir -p $HOME/app/uploaded_files && \
62
+ chown -R user:user $HOME/app/uploaded_files && \
63
+ chmod -R 777 $HOME/app/uploaded_files
64
+
65
+ # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰์„ ์‚ฌ์šฉ์ž ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰
66
+ USER user
67
+
68
+ # FastAPI ์‹คํ–‰ ๋ช…๋ น
69
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Dockerfile copy ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python 3.11 ์ด๋ฏธ์ง€ ์‚ฌ์šฉ
2
+ FROM python:3.11-slim
3
+
4
+ # ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ ์„ค์ •
5
+ WORKDIR /app
6
+
7
+ # ์‹œ์Šคํ…œ ํŒจํ‚ค์ง€ ์—…๋ฐ์ดํŠธ ๋ฐ ํ•„์ˆ˜ ํŒจํ‚ค์ง€ ์„ค์น˜
8
+ RUN apt-get update && apt-get install -y \
9
+ build-essential \
10
+ libmariadb-dev \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # ์˜์กด์„ฑ ํŒŒ์ผ ๋ณต์‚ฌ
14
+ COPY requirements.txt .
15
+
16
+ # Python ํŒจํ‚ค์ง€ ์„ค์น˜
17
+ RUN pip install --upgrade pip \
18
+ && pip install --no-cache-dir -r requirements.txt
19
+
20
+ # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ๋ณต์‚ฌ
21
+ COPY . .
22
+
23
+ # ํฌํŠธ ๋…ธ์ถœ (FastAPI ๊ธฐ๋ณธ ํฌํŠธ: 8000)
24
+ EXPOSE 8000
25
+
26
+ # FastAPI ์‹คํ–‰ ๋ช…๋ น
27
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Rag
3
+ emoji: ๐Ÿ“š
4
+ colorFrom: yellow
5
+ colorTo: yellow
6
+ sdk: docker
7
+ pinned: false
8
+ license: apache-2.0
9
+ short_description: rag
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app/__init__.py ADDED
File without changes
app/main.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from app.routers import upload,search,user
3
+ #from config.database import engine
4
+ # from models.user_info import Base
5
+
6
+ app = FastAPI()
7
+
8
+ # ๋ผ์šฐํ„ฐ ์ถ”๊ฐ€
9
+ app.include_router(upload.router)
10
+ app.include_router(search.router)
11
+ # app.include_router(user.router)
12
+
13
+
14
+
15
+ # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์ƒ์„ฑ
16
+ #Base.metadata.create_all(bind=engine)
app/modules/embedding.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ from langchain.document_loaders import PyPDFLoader
4
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
5
+ from langchain.vectorstores import FAISS
6
+ from langchain_huggingface.embeddings import HuggingFaceEmbeddings
7
+
8
+ async def process_and_store_file(file_path, user_id, websocket=None, upload_directory="./uploaded_files"):
9
+ try:
10
+ # 1. PDF ํŒŒ์ผ ๋กœ๋“œ
11
+ if websocket:
12
+ await websocket.send_text("1. PDF ํŒŒ์ผ ๋กœ๋“œ ์ค‘...")
13
+ loader = PyPDFLoader(file_path)
14
+ documents = loader.load()
15
+ if websocket:
16
+ await websocket.send_text(f"PDF ํŒŒ์ผ ๋กœ๋“œ ์™„๋ฃŒ: {len(documents)} ๋ฌธ์„œ")
17
+ except Exception as e:
18
+ if websocket:
19
+ await websocket.send_text(f"PDF ํŒŒ์ผ ๋กœ๋“œ ์˜ค๋ฅ˜: {e}")
20
+ return
21
+
22
+ try:
23
+ # 2. ํ…์ŠคํŠธ ๋ถ„ํ• 
24
+ if websocket:
25
+ await websocket.send_text("2. ํ…์ŠคํŠธ ๋ถ„ํ•  ์ค‘...")
26
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=3000, chunk_overlap=500)
27
+ docs = text_splitter.split_documents(documents)
28
+ if websocket:
29
+ await websocket.send_text(f"ํ…์ŠคํŠธ ๋ถ„ํ•  ์™„๋ฃŒ: {len(docs)} ์ฒญํฌ")
30
+ except Exception as e:
31
+ if websocket:
32
+ await websocket.send_text(f"ํ…์ŠคํŠธ ๋ถ„ํ•  ์˜ค๋ฅ˜: {e}")
33
+ return
34
+
35
+ try:
36
+ # 3. ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ ๋ฐ ๋ฒกํ„ฐํ™”
37
+ if websocket:
38
+ await websocket.send_text("3. ์ž„๋ฒ ๋”ฉ ์ƒ์„ฑ ๋ฐ ๋ฒกํ„ฐํ™” ์ค‘...")
39
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
40
+ vectors = FAISS.from_documents(docs, embeddings)
41
+
42
+ # 4. ๋ฒกํ„ฐ ์ €์žฅ
43
+ db_path = os.path.join(upload_directory, "faiss_index")
44
+ vectors.save_local(db_path)
45
+ if websocket:
46
+ await websocket.send_text(f"FAISS ์ธ๋ฑ์Šค ์ €์žฅ ์™„๋ฃŒ: {db_path}")
47
+ except Exception as e:
48
+ if websocket:
49
+ await websocket.send_text(f"๋ฒกํ„ฐํ™” ์˜ค๋ฅ˜: {e}")
50
+ return
51
+ finally:
52
+ # 5. ํŒŒ์ผ ์‚ญ์ œ
53
+ try:
54
+ if os.path.exists(file_path):
55
+ os.remove(file_path)
56
+ if websocket:
57
+ await websocket.send_text(f"ํŒŒ์ผ ์‚ญ์ œ ์™„๋ฃŒ: {file_path}")
58
+ except Exception as e:
59
+ if websocket:
60
+ await websocket.send_text(f"ํŒŒ์ผ ์‚ญ์ œ ์˜ค๋ฅ˜: {e}")
app/modules/evaluation.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import InferenceClient
3
+
4
+ class Evaluation:
5
+ def __init__(self, model: str = "Qwen/Qwen2.5-72B-Instruct"):
6
+ """
7
+ Args:
8
+ model (str): ์‚ฌ์šฉํ•  Hugging Face ๋ชจ๋ธ ์ด๋ฆ„ (๊ธฐ๋ณธ๊ฐ’: Qwen/Qwen2.5-72B-Instruct).
9
+ """
10
+ self.api_key = os.getenv("HF_API_KEY")
11
+ if not self.api_key:
12
+ raise ValueError("HF_API_KEY ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
13
+ self.client = InferenceClient(api_key=self.api_key)
14
+ self.model = model
15
+
16
+ def evaluate(self, instruction: str, answer: str) -> str:
17
+ """
18
+ ์‚ฌ์šฉ์ž์˜ ๋‹ต๋ณ€๊ณผ ํ‰๊ฐ€ ๊ธฐ์ค€์„ ๊ธฐ๋ฐ˜์œผ๋กœ AI ๋ชจ๋ธ ํ‰๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
19
+
20
+ Args:
21
+ instruction (str): ํ‰๊ฐ€ ๊ธฐ์ค€์ด ํฌํ•จ๋œ ์ง€์นจ.
22
+ answer (str): ์‚ฌ์šฉ์ž ๋‹ต๋ณ€.
23
+
24
+ Returns:
25
+ str: ํ‰๊ฐ€ ๊ฒฐ๊ณผ.
26
+ """
27
+ messages = [
28
+ {"role": "system", "content": "์ˆ˜์—…๋„๊ตฌ ๊ตฌ์„ฑ ๋งˆ๋ฒ•์‚ฌ์ž…๋‹ˆ๋‹ค. ํ€ด์ฆˆ, ๊ณผ์ œ, ํ† ๋ก ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."},
29
+ {"role": "user", "content": instruction},
30
+ ]
31
+
32
+ try:
33
+ stream = self.client.chat.completions.create(
34
+ model=self.model,
35
+ messages=messages,
36
+ temperature=0.2,
37
+ max_tokens=2048,
38
+ top_p=0.7,
39
+ stream=True,
40
+ )
41
+
42
+ result = ""
43
+ for chunk in stream:
44
+ if "delta" in chunk.choices[0]:
45
+ result += chunk.choices[0].delta.content
46
+ print(f"Intermediate result: {result}") # ๋””๋ฒ„๊น…์šฉ ์ถœ๋ ฅ
47
+
48
+ return result.strip()
49
+
50
+ except Exception as e:
51
+ error_message = f"An error occurred during evaluation: {e}"
52
+ print(error_message) # ๋””๋ฒ„๊น…์šฉ ์ถœ๋ ฅ
53
+ return error_message
54
+
55
+ async def evaluate_stream(self, instruction: str):
56
+ """
57
+ ๋น„๋™๊ธฐ ๋ฐฉ์‹์œผ๋กœ ํ‰๊ฐ€ ๊ฒฐ๊ณผ๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
58
+
59
+ Args:
60
+ instruction (str): ํ‰๊ฐ€ ๊ธฐ์ค€์ด ํฌํ•จ๋œ ์ง€์นจ.
61
+
62
+ Yields:
63
+ str: ์‹ค์‹œ๊ฐ„ ํ‰๊ฐ€ ๊ฒฐ๊ณผ.
64
+ """
65
+ messages = [
66
+ {"role": "system", "content": "์„ ์ƒ๋‹˜์—๊ฒŒ ๊ผญ ํ•„์š”ํ•œ ์ˆ˜์—…๋„๊ตฌ ๊ตฌ์„ฑ ๋งˆ๋ฒ•์‚ฌ์ž…๋‹ˆ๋‹ค."},
67
+ {"role": "user", "content": instruction},
68
+ ]
69
+
70
+ try:
71
+ stream = self.client.chat.completions.create(
72
+ model=self.model,
73
+ messages=messages,
74
+ temperature=0.2,
75
+ max_tokens=2048,
76
+ top_p=0.7,
77
+ stream=True,
78
+ )
79
+
80
+ for chunk in stream:
81
+ if "delta" in chunk.choices[0]:
82
+ content = chunk.choices[0].delta.content
83
+ print(f"Streaming result: {content}") # ๋””๋ฒ„๊น…์šฉ ์ถœ๋ ฅ
84
+ yield content
85
+
86
+ except Exception as e:
87
+ error_message = f"Error: {e}"
88
+ print(error_message) # ๋””๋ฒ„๊น…์šฉ ์ถœ๋ ฅ
89
+ yield error_message
app/routers/__init__.py ADDED
File without changes
app/routers/search.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request, Form
2
+ from fastapi.responses import HTMLResponse, StreamingResponse
3
+ from fastapi.templating import Jinja2Templates
4
+ from langchain.vectorstores import FAISS
5
+ from langchain.embeddings import HuggingFaceEmbeddings
6
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
7
+ from app.modules.evaluation import Evaluation # ํ‰๊ฐ€ ๋ชจ๋“ˆ ์ž„ํฌํŠธ
8
+ import os
9
+
10
+ router = APIRouter()
11
+
12
+ # ๋กœ์ปฌ ๋ชจ๋ธ ๋กœ๋“œ (HuggingFace T5 ์‚ฌ์šฉ)
13
+ # tokenizer = AutoTokenizer.from_pretrained("gogamza/kobart-base-v2")
14
+ # model = AutoModelForSeq2SeqLM.from_pretrained("gogamza/kobart-base-v2")
15
+
16
+ # def summarize_text(text: str, max_length=150):
17
+ # inputs = tokenizer.encode("summarize: " + text, return_tensors="pt", max_length=512, truncation=True)
18
+ # outputs = model.generate(inputs, max_length=512, min_length=50, length_penalty=2.0, num_beams=4, early_stopping=True)
19
+ # return tokenizer.decode(outputs[0], skip_special_tokens=True)
20
+
21
+ # ํ…œํ”Œ๋ฆฟ ์„ค์ •
22
+ templates = Jinja2Templates(directory="app/templates")
23
+
24
+ # ์—…๋กœ๋“œ๋œ ํŒŒ์ผ๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฒฝ๋กœ ์„ค์ •
25
+ UPLOAD_DIRECTORY = "./uploaded_files"
26
+ db_path = os.path.join(UPLOAD_DIRECTORY, "faiss_index")
27
+
28
+ # ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋กœ๋“œ
29
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
30
+
31
+ @router.get("/search", response_class=HTMLResponse)
32
+ async def search_page(request: Request):
33
+ return templates.TemplateResponse("search.html", {"request": request})
34
+
35
+
36
+ @router.post("/search/stream")
37
+ async def search_stream(query: str = Form(...), model: str = Form(...)):
38
+ # ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋กœ๋“œ
39
+ vector_db = FAISS.load_local(db_path, embeddings, allow_dangerous_deserialization=True)
40
+ results = vector_db.similarity_search(query, k=5)
41
+
42
+ # ๊ฒ€์ƒ‰๋œ ๊ฒฐ๊ณผ ํ…์ŠคํŠธ๋ฅผ ์—ฐ๊ฒฐ
43
+ full_text = "\n\n".join([result.page_content for result in results])
44
+
45
+ # ํ‰๊ฐ€ ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™”
46
+ evaluator = Evaluation(model=model)
47
+
48
+ # ํ‰๊ฐ€ ๊ธฐ์ค€
49
+ instruction = (
50
+ f"๋‹ค์Œ ํ…์ŠคํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ {query}\n\n{full_text}"
51
+ )
52
+
53
+
54
+ async def stream():
55
+ async for data in evaluator.evaluate_stream(instruction):
56
+ yield data
57
+
58
+ return StreamingResponse(stream(), media_type="text/plain")
app/routers/upload.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, File, UploadFile, Request, WebSocket, WebSocketDisconnect
2
+ from fastapi.responses import HTMLResponse, RedirectResponse
3
+ from fastapi.templating import Jinja2Templates
4
+ from app.modules.embedding import process_and_store_file
5
+ import shutil
6
+ import os
7
+ import uuid
8
+ import asyncio
9
+
10
+ router = APIRouter()
11
+
12
+ # ์—…๋กœ๋“œ๋œ ํŒŒ์ผ์„ ์ €์žฅํ•  ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
13
+ UPLOAD_DIRECTORY = "./uploaded_files"
14
+ if not os.path.exists(UPLOAD_DIRECTORY):
15
+ os.makedirs(UPLOAD_DIRECTORY, exist_ok=True)
16
+ os.chmod(UPLOAD_DIRECTORY, 0o777)
17
+
18
+ # ํ…œํ”Œ๋ฆฟ ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
19
+ templates = Jinja2Templates(directory="app/templates")
20
+
21
+ # WebSocket ์—ฐ๊ฒฐ ๊ด€๋ฆฌ
22
+ connections = {}
23
+
24
+ @router.get("/", response_class=HTMLResponse)
25
+ async def main(request: Request):
26
+ return templates.TemplateResponse("upload.html", {"request": request})
27
+
28
+ @router.post("/uploadfile/{user_id}/")
29
+ async def upload_file(user_id: str, file: UploadFile = File(...)):
30
+ # ๊ณ ์œ  ํŒŒ์ผ ์ด๋ฆ„ ์ƒ์„ฑ
31
+ unique_filename = f"{uuid.uuid4()}_{file.filename}"
32
+ file_location = os.path.join(UPLOAD_DIRECTORY, unique_filename)
33
+
34
+ # ํŒŒ์ผ ์ €์žฅ
35
+ try:
36
+ with open(file_location, "wb") as buffer:
37
+ shutil.copyfileobj(file.file, buffer)
38
+ print(f"File saved at {file_location}")
39
+ except Exception as e:
40
+ return {"error": f"ํŒŒ์ผ ์ €์žฅ ์˜ค๋ฅ˜: {e}"}
41
+
42
+ # ๋น„๋™๊ธฐ ์ž‘์—… ์‹คํ–‰
43
+ asyncio.create_task(process_and_store_file(file_location, user_id, connections.get(user_id), UPLOAD_DIRECTORY))
44
+
45
+ return {"info": f"File '{file.filename}' uploaded successfully."}
46
+
47
+ @router.websocket("/ws/{user_id}")
48
+ async def websocket_endpoint(websocket: WebSocket, user_id: str):
49
+ await websocket.accept()
50
+ connections[user_id] = websocket # WebSocket ์—ฐ๊ฒฐ ์ €์žฅ
51
+ try:
52
+ while True:
53
+ data = await websocket.receive_text()
54
+ print(f"Received from {user_id}: {data}")
55
+ except WebSocketDisconnect:
56
+ print(f"User {user_id} disconnected")
57
+ del connections[user_id] # ์—ฐ๊ฒฐ ์ œ๊ฑฐ
58
+ except Exception as e:
59
+ print(f"Error with user {user_id}: {e}")
60
+ del connections[user_id] # ์—ฐ๊ฒฐ ์ œ๊ฑฐ
app/routers/user.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # from fastapi import APIRouter, Depends, HTTPException
2
+ # from sqlalchemy.orm import Session
3
+ # from models.user_info import UserInfo
4
+ # from config.database import get_db
5
+ # from typing import Optional
6
+ # from datetime import datetime
7
+
8
+ # router = APIRouter()
9
+
10
+ # # ์‚ฌ์šฉ์ž ์กฐํšŒ
11
+ # @router.get("/users/{user_id}")
12
+ # async def get_user(user_id: str, db: Session = Depends(get_db)):
13
+ # user = db.query(UserInfo).filter(UserInfo.id == user_id).first()
14
+ # if not user:
15
+ # raise HTTPException(status_code=404, detail="User not found")
16
+ # return user
17
+
18
+ # # ์‚ฌ์šฉ์ž ์ƒ์„ฑ
19
+ # @router.post("/users")
20
+ # async def create_user(
21
+ # id: str,
22
+ # preferred_username: Optional[str] = None,
23
+ # mobile: Optional[str] = None,
24
+ # business_cd: Optional[str] = None,
25
+ # role_seq: Optional[int] = None,
26
+ # name: Optional[str] = None,
27
+ # email: Optional[str] = None,
28
+ # profile: Optional[str] = None,
29
+ # school_code: Optional[str] = None,
30
+ # grade: Optional[int] = None,
31
+ # class_name: Optional[str] = None,
32
+ # student_no: Optional[int] = None,
33
+ # creator_id: Optional[str] = None,
34
+ # creator_ip: Optional[str] = None,
35
+ # db: Session = Depends(get_db)
36
+ # ):
37
+ # new_user = UserInfo(
38
+ # id=id,
39
+ # preferred_username=preferred_username,
40
+ # mobile=mobile,
41
+ # business_cd=business_cd,
42
+ # role_seq=role_seq,
43
+ # name=name,
44
+ # email=email,
45
+ # profile=profile,
46
+ # school_code=school_code,
47
+ # grade=grade,
48
+ # class_name=class_name,
49
+ # student_no=student_no,
50
+ # created_at=datetime.now(),
51
+ # creator_id=creator_id,
52
+ # creator_ip=creator_ip,
53
+ # )
54
+ # db.add(new_user)
55
+ # db.commit()
56
+ # db.refresh(new_user)
57
+ # return new_user
58
+
59
+ # # ์‚ฌ์šฉ์ž ๋ชฉ๋ก ์กฐํšŒ
60
+ # @router.get("/users")
61
+ # async def list_users(db: Session = Depends(get_db)):
62
+ # return db.query(UserInfo).all()
63
+
64
+ # # ์‚ฌ์šฉ์ž ์—…๋ฐ์ดํŠธ
65
+ # @router.put("/users/{user_id}")
66
+ # async def update_user(
67
+ # user_id: str,
68
+ # preferred_username: Optional[str] = None,
69
+ # mobile: Optional[str] = None,
70
+ # business_cd: Optional[str] = None,
71
+ # role_seq: Optional[int] = None,
72
+ # name: Optional[str] = None,
73
+ # email: Optional[str] = None,
74
+ # profile: Optional[str] = None,
75
+ # school_code: Optional[str] = None,
76
+ # grade: Optional[int] = None,
77
+ # class_name: Optional[str] = None,
78
+ # student_no: Optional[int] = None,
79
+ # updater_id: Optional[str] = None,
80
+ # updater_ip: Optional[str] = None,
81
+ # db: Session = Depends(get_db)
82
+ # ):
83
+ # user = db.query(UserInfo).filter(UserInfo.id == user_id).first()
84
+ # if not user:
85
+ # raise HTTPException(status_code=404, detail="User not found")
86
+
87
+ # # ํ•„๋“œ ์—…๋ฐ์ดํŠธ
88
+ # if preferred_username is not None:
89
+ # user.preferred_username = preferred_username
90
+ # if mobile is not None:
91
+ # user.mobile = mobile
92
+ # if business_cd is not None:
93
+ # user.business_cd = business_cd
94
+ # if role_seq is not None:
95
+ # user.role_seq = role_seq
96
+ # if name is not None:
97
+ # user.name = name
98
+ # if email is not None:
99
+ # user.email = email
100
+ # if profile is not None:
101
+ # user.profile = profile
102
+ # if school_code is not None:
103
+ # user.school_code = school_code
104
+ # if grade is not None:
105
+ # user.grade = grade
106
+ # if class_name is not None:
107
+ # user.class_name = class_name
108
+ # if student_no is not None:
109
+ # user.student_no = student_no
110
+
111
+ # user.updated_at = datetime.now()
112
+ # user.updater_id = updater_id
113
+ # user.updater_ip = updater_ip
114
+
115
+ # db.commit()
116
+ # db.refresh(user)
117
+ # return user
118
+
119
+ # # ์‚ฌ์šฉ์ž ์‚ญ์ œ
120
+ # @router.delete("/users/{user_id}")
121
+ # async def delete_user(user_id: str, db: Session = Depends(get_db)):
122
+ # user = db.query(UserInfo).filter(UserInfo.id == user_id).first()
123
+ # if not user:
124
+ # raise HTTPException(status_code=404, detail="User not found")
125
+ # db.delete(user)
126
+ # db.commit()
127
+ # return {"message": "User deleted successfully"}
app/templates/search.html ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>๋ฌธ์„œ ๊ฒ€์ƒ‰</title>
7
+ <style>
8
+ .container {
9
+ width: 50%;
10
+ margin: 0 auto;
11
+ padding: 20px;
12
+ text-align: center;
13
+ border: 1px solid #ccc;
14
+ border-radius: 8px;
15
+ background-color: #f9f9f9;
16
+ }
17
+ .search-bar {
18
+ display: flex;
19
+ justify-content: center;
20
+ align-items: center;
21
+ gap: 10px;
22
+ }
23
+ input[type="text"], select {
24
+ padding: 10px;
25
+ border: 1px solid #ccc;
26
+ border-radius: 4px;
27
+ font-size: 14px;
28
+ }
29
+ input[type="text"] {
30
+ flex: 1;
31
+ }
32
+ select {
33
+ flex: 0.5;
34
+ }
35
+ button {
36
+ padding: 10px 20px;
37
+ background-color: #007bff;
38
+ color: white;
39
+ border: none;
40
+ border-radius: 4px;
41
+ cursor: pointer;
42
+ font-size: 14px;
43
+ }
44
+ button:hover {
45
+ background-color: #0056b3;
46
+ }
47
+ .results, .evaluation, .stream-output {
48
+ margin-top: 5px;
49
+ }
50
+ .stream-output {
51
+ text-align: left;
52
+ white-space: pre-wrap;
53
+ border: 1px solid #ccc;
54
+ padding: 10px;
55
+ height: 500px;
56
+ overflow-y: scroll;
57
+ }
58
+ nav {
59
+ background-color: #333;
60
+ padding: 10px;
61
+ text-align: center;
62
+ }
63
+ nav ul {
64
+ list-style-type: none;
65
+ padding: 0;
66
+ margin: 0;
67
+ }
68
+ nav ul li {
69
+ display: inline;
70
+ margin: 0 15px;
71
+ }
72
+ nav ul li a {
73
+ color: white;
74
+ text-decoration: none;
75
+ font-weight: bold;
76
+ }
77
+ nav ul li a:hover {
78
+ text-decoration: underline;
79
+ }
80
+ </style>
81
+ </head>
82
+ <body>
83
+ <header>
84
+ <nav>
85
+ <ul>
86
+ <li><a href="/search">๋ฌธ์„œ ๊ฒ€์ƒ‰</a></li>
87
+ <li><a href="/">ํŒŒ์ผ ์—…๋กœ๋“œ</a></li>
88
+ </ul>
89
+ </nav>
90
+ </header>
91
+ <div class="container">
92
+ <h2>๋ฌธ์„œ ๊ฒ€์ƒ‰</h2>
93
+ <form id="search-form" class="search-bar">
94
+ <input type="text" id="query" name="query" placeholder="๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”..." required>
95
+ <select id="model" name="model">
96
+ <option value="Qwen/Qwen2.5-72B-Instruct">Qwen/Qwen2.5-72B-Instruct</option>
97
+ <option value="meta-llama/Llama-3.3-70B-Instruct">meta-llama/Llama-3.3-70B-Instruct</option>
98
+ <option value="CohereForAI/c4ai-command-r-plus-08-2024">CohereForAI/c4ai-command-r-plus-08-2024</option>
99
+ <option value="Qwen/QwQ-32B-Preview">Qwen/QwQ-32B-Preview</option>
100
+ <option value="nvidia/Llama-3.1-Nemotron-70B-Instruct-HF">nvidia/Llama-3.1-Nemotron-70B-Instruct-HF</option>
101
+ <option value="Qwen/Qwen2.5-Coder-32B-Instruct">Qwen/Qwen2.5-Coder-32B-Instruct</option>
102
+ <option value="meta-llama/Llama-3.2-11B-Vision-Instruct">meta-llama/Llama-3.2-11B-Vision-Instruct</option>
103
+ <option value="NousResearch/Hermes-3-Llama-3.1-8B">NousResearch/Hermes-3-Llama-3.1-8B</option>
104
+ <option value="mistralai/Mistral-Nemo-Instruct-2407">mistralai/Mistral-Nemo-Instruct-2407</option>
105
+ <option value="microsoft/Phi-3.5-mini-instruct">microsoft/Phi-3.5-mini-instruct</option>
106
+ </select>
107
+ <button type="submit">๊ฒ€์ƒ‰</button>
108
+ </form>
109
+ <h3>์‹ค์‹œ๊ฐ„ ๊ฒฐ๊ณผ</h3>
110
+ <div id="stream-output" class="stream-output">
111
+
112
+ <div id="stream-content">
113
+ <!-- ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค -->
114
+ </div>
115
+ </div>
116
+ </div>
117
+
118
+ <script>
119
+ let currentController = null; // ํ˜„์žฌ ์š”์ฒญ์„ ์ œ์–ดํ•  AbortController
120
+
121
+ document.getElementById("search-form").addEventListener("submit", async (event) => {
122
+ event.preventDefault(); // ๊ธฐ๋ณธ ํผ ์ œ์ถœ ๋ฐฉ์ง€
123
+ const query = document.getElementById("query").value;
124
+ const model = document.getElementById("model").value;
125
+ const outputContainer = document.getElementById("stream-content");
126
+
127
+ // ์ด์ „ ์š”์ฒญ ์ค‘๋‹จ
128
+ if (currentController) {
129
+ currentController.abort();
130
+ }
131
+ currentController = new AbortController();
132
+ const signal = currentController.signal;
133
+
134
+ // ์ถœ๋ ฅ ์ดˆ๊ธฐํ™”
135
+ outputContainer.textContent = "๊ฒ€์ƒ‰ ์ค‘...\n";
136
+
137
+ try {
138
+ const response = await fetch("/search/stream", {
139
+ method: "POST",
140
+ headers: {
141
+ "Content-Type": "application/x-www-form-urlencoded"
142
+ },
143
+ body: new URLSearchParams({ query, model }),
144
+ signal, // AbortController์˜ signal ์ „๋‹ฌ
145
+ });
146
+
147
+ const reader = response.body.getReader();
148
+ const decoder = new TextDecoder("utf-8");
149
+
150
+ while (true) {
151
+ const { value, done } = await reader.read();
152
+ if (done) break;
153
+ outputContainer.textContent += decoder.decode(value);
154
+ outputContainer.scrollTop = outputContainer.scrollHeight; // ์Šคํฌ๋กค ์ž๋™ ์ด๋™
155
+ }
156
+ } catch (error) {
157
+ if (error.name === "AbortError") {
158
+ outputContainer.textContent += "\n์š”์ฒญ์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n";
159
+ } else {
160
+ outputContainer.textContent = `์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`;
161
+ }
162
+ } finally {
163
+ currentController = null; // ํ˜„์žฌ ์š”์ฒญ ์™„๋ฃŒ ํ›„ ์ดˆ๊ธฐํ™”
164
+ }
165
+ });
166
+ </script>
167
+ </body>
168
+ </html>
app/templates/upload.html ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ํŒŒ์ผ ์—…๋กœ๋“œ</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ background-color: #f4f4f4;
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+ .container {
15
+ width: 50%;
16
+ margin: 50px auto;
17
+ padding: 20px;
18
+ text-align: center;
19
+ border: 1px solid #ccc;
20
+ border-radius: 8px;
21
+ background-color: #ffffff;
22
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
23
+ }
24
+ input[type="file"] {
25
+ margin: 10px 0;
26
+ padding: 10px;
27
+ }
28
+ button {
29
+ padding: 10px 20px;
30
+ background-color: #007bff;
31
+ color: white;
32
+ border: none;
33
+ border-radius: 4px;
34
+ cursor: pointer;
35
+ font-size: 16px;
36
+ }
37
+ button:hover {
38
+ background-color: #0056b3;
39
+ }
40
+ .status {
41
+ margin-top: 20px;
42
+ text-align: left;
43
+ font-size: 14px;
44
+ }
45
+ .status p {
46
+ background-color: #e9f4ff;
47
+ border-left: 4px solid #007bff;
48
+ padding: 10px;
49
+ margin: 5px 0;
50
+ }
51
+ nav {
52
+ background-color: #333;
53
+ padding: 10px;
54
+ text-align: center;
55
+ }
56
+ nav ul {
57
+ list-style-type: none;
58
+ padding: 0;
59
+ margin: 0;
60
+ }
61
+ nav ul li {
62
+ display: inline;
63
+ margin: 0 15px;
64
+ }
65
+ nav ul li a {
66
+ color: white;
67
+ text-decoration: none;
68
+ font-weight: bold;
69
+ }
70
+ nav ul li a:hover {
71
+ text-decoration: underline;
72
+ }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <header>
77
+ <nav>
78
+ <ul>
79
+ <li><a href="/search">๋ฌธ์„œ ๊ฒ€์ƒ‰</a></li>
80
+ <li><a href="/">ํŒŒ์ผ ์—…๋กœ๋“œ</a></li>
81
+ </ul>
82
+ </nav>
83
+ </header>
84
+ <div class="container">
85
+ <h2>ํŒŒ์ผ ์—…๋กœ๋“œ</h2>
86
+ <form id="upload-form" enctype="multipart/form-data">
87
+ <input type="file" name="file" required>
88
+ <button type="submit">์—…๋กœ๋“œ</button>
89
+ </form>
90
+ <div class="status" id="status">
91
+ <h3>์—…๋กœ๋“œ ์ƒํƒœ</h3>
92
+ </div>
93
+ </div>
94
+
95
+ <script>
96
+ const form = document.getElementById('upload-form');
97
+ const statusDiv = document.getElementById('status');
98
+ const userId = "user123"; // ๊ณ ์œ  ์‚ฌ์šฉ์ž ID (๋™์ ์œผ๋กœ ์„ค์ • ๊ฐ€๋Šฅ)
99
+
100
+ // WebSocket ์„ค์ •
101
+ const socket = new WebSocket(`ws://127.0.0.1:8000/ws/${userId}`);
102
+
103
+ socket.onmessage = (event) => {
104
+ const message = event.data;
105
+ const p = document.createElement('p');
106
+ p.textContent = message;
107
+ statusDiv.appendChild(p);
108
+ };
109
+
110
+ socket.onerror = () => {
111
+ const p = document.createElement('p');
112
+ p.textContent = "WebSocket ์—ฐ๊ฒฐ ์˜ค๋ฅ˜.";
113
+ p.style.color = "red";
114
+ statusDiv.appendChild(p);
115
+ };
116
+
117
+ form.addEventListener('submit', async (e) => {
118
+ e.preventDefault();
119
+ statusDiv.innerHTML = '<h3>์—…๋กœ๋“œ ์ƒํƒœ</h3>';
120
+ const formData = new FormData(form);
121
+
122
+
123
+ // ํŒŒ์ผ ์—…๋กœ๋“œ ์š”์ฒญ
124
+ const response = await fetch(`/uploadfile/${userId}/`, {
125
+ method: "POST",
126
+ body: formData,
127
+ });
128
+
129
+ if (response.ok) {
130
+ const p = document.createElement('p');
131
+ p.textContent = "ํŒŒ์ผ ์—…๋กœ๋“œ ์„ฑ๊ณต!";
132
+ statusDiv.appendChild(p);
133
+ } else {
134
+ const p = document.createElement('p');
135
+ p.textContent = "ํŒŒ์ผ ์—…๋กœ๋“œ ์‹คํŒจ.";
136
+ p.style.color = "red";
137
+ statusDiv.appendChild(p);
138
+ }
139
+ });
140
+ </script>
141
+ </body>
142
+ </html>
app/uploaded_files copy/a.txt ADDED
File without changes
config/database.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import os
2
+ # from sqlalchemy import create_engine
3
+ # from sqlalchemy.ext.declarative import declarative_base
4
+ # from sqlalchemy.orm import sessionmaker
5
+ # from sqlalchemy.exc import SQLAlchemyError
6
+
7
+ # # MySQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •
8
+ # DATABASE_URL = os.getenv("DATABASE_URL", "mysql+mysqlconnector://root:root@10.10.10.180:3306/chathess")
9
+
10
+ # # SQLAlchemy ์—”์ง„ ์ƒ์„ฑ (์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€)
11
+ # try:
12
+ # engine = create_engine(DATABASE_URL)
13
+ # print("Database engine created successfully.")
14
+ # except SQLAlchemyError as e:
15
+ # print("Failed to create database engine.")
16
+ # print("Error:", e)
17
+ # engine = None
18
+
19
+ # # ์„ธ์…˜ ์ƒ์„ฑ (์—”์ง„์ด None์ด๋ฉด ์„ธ์…˜ ์ดˆ๊ธฐํ™” ์•ˆ ํ•จ)
20
+ # if engine:
21
+ # SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
22
+ # else:
23
+ # SessionLocal = None
24
+
25
+ # # Base ํด๋ž˜์Šค ์ƒ์„ฑ
26
+ # Base = declarative_base()
27
+
28
+ # # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ธ์…˜ ์˜์กด์„ฑ
29
+ # def get_db():
30
+ # if not SessionLocal:
31
+ # print("Database session is not available.")
32
+ # raise RuntimeError("Database is not initialized.")
33
+
34
+ # db = SessionLocal()
35
+ # try:
36
+ # yield db
37
+ # finally:
38
+ # db.close()
39
+
gradio/app.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import gradio as gr
2
+ # from transformers import pipeline
3
+
4
+ # # Hugging Face ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ชจ๋ธ ๋กœ๋“œ
5
+ # image_generator = pipeline("image-generation", model="CompVis/stable-diffusion-v1-4")
6
+
7
+ # # ํ•จ์ˆ˜ ์ •์˜
8
+ # def generate_image(prompt):
9
+ # result = image_generator(prompt)
10
+ # return result[0]["image"]
11
+
12
+ # # Gradio ์ธํ„ฐํŽ˜์ด์Šค
13
+ # demo = gr.Interface(
14
+ # fn=generate_image, # ํ•จ์ˆ˜ ์—ฐ๊ฒฐ
15
+ # inputs=gr.Textbox(label="Image Prompt"), # ์ž…๋ ฅ ํ…์ŠคํŠธ ๋ฐ•์Šค
16
+ # outputs="image", # ์ถœ๋ ฅ ์ด๋ฏธ์ง€
17
+ # title="Image Generation with Stable Diffusion",
18
+ # description="Enter a prompt, and the Stable Diffusion model will generate an image.",
19
+ # )
20
+
21
+ # # ์ธํ„ฐํŽ˜์ด์Šค ์‹คํ–‰
22
+ # if __name__ == "__main__":
23
+ # demo.launch()
models/user_info.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # from sqlalchemy import Column, String, BigInteger, DateTime, ForeignKey
2
+ # from config.database import Base
3
+ # from sqlalchemy.dialects.mysql import TINYINT # MySQL TINYINT ํƒ€์ž…
4
+
5
+ # class UserInfo(Base):
6
+ # __tablename__ = "USER_INFO"
7
+
8
+ # id = Column(String(36), primary_key=True, comment="ID")
9
+ # preferred_username = Column(String(50), nullable=True, comment="๋กœ๊ทธ์ธ์•„์ด๋””")
10
+ # mobile = Column(String(15), nullable=True, comment="๋ชจ๋ฐ”์ผ")
11
+ # business_cd = Column(String(8), nullable=True, comment="์†Œ์†๊ธฐ๊ด€์ผ๋ จ๋ฒˆ")
12
+ # role_seq = Column(BigInteger, ForeignKey("ROLE.SEQ"), nullable=True, comment="์—ญํ• ์ผ๋ จ๋ฒˆํ˜ธ")
13
+ # name = Column(String(255), nullable=True, comment="์‚ฌ์šฉ์ž์ด๋ฆ„")
14
+ # email = Column(String(255), nullable=True, comment="์ด๋ฉ”์ผ")
15
+ # profile = Column(String(255), nullable=True, comment="ํ”„๋กœํ•„")
16
+ # school_code = Column(String(50), nullable=True, comment="ํ•™๊ต์ฝ”๋“œ")
17
+ # grade = Column(TINYINT, nullable=True, comment="ํ•™๋…„")
18
+ # class_name = Column(String(50), nullable=True, comment="ํ•™๊ธ‰")
19
+ # student_no = Column(TINYINT, nullable=True, comment="ํ•™์ƒ๋ฒˆํ˜ธ")
20
+ # created_at = Column(DateTime, nullable=False, comment="์ƒ์„ฑ์ผ")
21
+ # creator_id = Column(String(36), nullable=True, comment="์ƒ์„ฑ์ž์•„์ด๋””")
22
+ # creator_ip = Column(String(50), nullable=True, comment="์ƒ์„ฑ์žIP")
23
+ # updated_at = Column(DateTime, nullable=True, comment="์ˆ˜์ •์ผ")
24
+ # updater_id = Column(String(36), nullable=True, comment="์ˆ˜์ •์ž์•„์ด๋””")
25
+ # updater_ip = Column(String(50), nullable=True, comment="์ˆ˜์ •์žIP")
readme.txt ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ (Windows ํ™˜๊ฒฝ)
2
+
3
+ # 1. ํ”„๋กœ์ ํŠธ ํด๋” ์ƒ์„ฑ ๋ฐ ํŒŒ์ผ ๊ตฌ์„ฑ (Windows ๋ช…๋ น์–ด)
4
+ # - ํ”„๋กœ์ ํŠธ ํด๋”: fastapi_upload_project
5
+ # - ํด๋” ๊ตฌ์กฐ:
6
+ RAG/
7
+ โ”œโ”€โ”€ .venv/ # ๊ฐ€์ƒํ™˜๊ฒฝ ํด๋”
8
+ โ”œโ”€โ”€ .vscode/ # VSCode ์„ค์ • ํด๋”
9
+ โ”œโ”€โ”€ app/ # FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋”
10
+ โ”‚ โ”œโ”€โ”€ __pycache__/ # ์บ์‹œ ํŒŒ์ผ (์ž๋™ ์ƒ์„ฑ)
11
+ โ”‚ โ”œโ”€โ”€ resources/ # ์ถ”๊ฐ€์ ์ธ ๋ฆฌ์†Œ์Šค (XML ๋“ฑ)
12
+ โ”‚ โ”œโ”€โ”€ routers/ # ๋ผ์šฐํ„ฐ ํด๋”
13
+ โ”‚ โ”‚ โ”œโ”€โ”€ __init__.py # ๋ผ์šฐํ„ฐ ์ดˆ๊ธฐํ™” ํŒŒ์ผ
14
+ โ”‚ โ”‚ โ”œโ”€โ”€ user.py # ์‚ฌ์šฉ์ž ๊ด€๋ จ ๋ผ์šฐํ„ฐ
15
+ โ”‚ โ”‚ โ”œโ”€โ”€ search.py # ๊ฒ€์ƒ‰ ๊ด€๋ จ ๋ผ์šฐํ„ฐ
16
+ โ”‚ โ”‚ โ”œโ”€โ”€ upload.py # ์—…๋กœ๋“œ ๊ด€๋ จ ๋ผ์šฐํ„ฐ
17
+ โ”‚ โ”œโ”€โ”€ templates/ # HTML ํ…œํ”Œ๋ฆฟ ํด๋”
18
+ โ”‚ โ”‚ โ”œโ”€โ”€ upload.html # ์—…๋กœ๋“œ ํŽ˜์ด์ง€
19
+ โ”‚ โ”‚ โ”œโ”€โ”€ search.html # ๊ฒ€์ƒ‰ ํŽ˜์ด์ง€
20
+ โ”‚ โ”œโ”€โ”€ main.py # FastAPI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ง„์ž…์ 
21
+ โ”œโ”€โ”€ config/ # ์„ค์ • ๊ด€๋ จ ํด๋”
22
+ โ”‚ โ”œโ”€โ”€ __pycache__/ # ์บ์‹œ ํŒŒ์ผ (์ž๋™ ์ƒ์„ฑ)
23
+ โ”‚ โ”œโ”€โ”€ database.py # SQLAlchemy ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •
24
+ โ”‚ โ”œโ”€โ”€ mybatis_manager.py # XML ๋งคํผ ๋ฐ SQL ์‹คํ–‰ ๊ด€๋ฆฌ
25
+ โ”œโ”€โ”€ mapper/ # MyBatis XML ๋งคํผ ํด๋”
26
+ โ”‚ โ”œโ”€โ”€ user_mapper.xml # ์‚ฌ์šฉ์ž ๋งคํผ XML
27
+ โ”‚ โ”œโ”€โ”€ order_mapper.xml # ์ฃผ๋ฌธ ๋งคํผ XML
28
+ โ”‚ โ”œโ”€โ”€ product_mapper.xml # ์ƒํ’ˆ ๋งคํผ XML
29
+ โ”œโ”€โ”€ models/ # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ชจ๋ธ ํด๋”
30
+ โ”‚ โ”œโ”€โ”€ __pycache__/ # ์บ์‹œ ํŒŒ์ผ (์ž๋™ ์ƒ์„ฑ)
31
+ โ”‚ โ”œโ”€โ”€ user_info.py # ์‚ฌ์šฉ์ž ์ •๋ณด ๋ชจ๋ธ
32
+ โ”œโ”€โ”€ uploaded_files/ # ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ์ €์žฅ์†Œ
33
+ โ”œโ”€โ”€ .gitignore # Git ๋ฌด์‹œ ํŒŒ์ผ
34
+ โ”œโ”€โ”€ requirements.txt # Python ํŒจํ‚ค์ง€ ์˜์กด์„ฑ
35
+ โ”œโ”€โ”€ readme.txt # ํ”„๋กœ์ ํŠธ ์„ค๋ช… ํŒŒ์ผ
36
+
37
+
38
+ # ๋ผ์šฐํ„ฐ ์ถ”๊ฐ€
39
+ # main.py ํŒŒ์ผ์— ๋ผ์šฐํ„ฐ ์ถ”๊ฐ€
40
+ app.include_router(upload.router)
41
+
42
+ # FastAPI ๋ผ์šฐํ„ฐ ํŒŒ์ผ ์ž‘์„ฑ
43
+ # app/routers/upload.py ํŒŒ์ผ์— ํŒŒ์ผ ์—…๋กœ๋“œ ๊ด€๋ จ ๋ผ์šฐํ„ฐ ์ž‘์„ฑ
44
+
45
+ -------------------------------------------------------------------------
46
+ # ๊ฐ€์ƒ ํ™˜๊ฒฝ ์ƒ์„ฑ ๋ฐ ํŒจํ‚ค์ง€ ์„ค์น˜ (Windows)
47
+ # Python ๊ฐ€์ƒ ํ™˜๊ฒฝ์„ ์ƒ์„ฑํ•˜๊ณ  ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜์„ธ์š”.
48
+ #
49
+ # python -m venv rag
50
+ # rag\Scripts\activate
51
+ # ./rag/scripts/activate.ps1(powershell evn switching)
52
+ # pip install -r requirements.txt
53
+
54
+ # FastAPI ์„œ๋ฒ„ ์‹คํ–‰
55
+ # FastAPI ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”.
56
+ # uvicorn app.main:app --reload
57
+ # pytorch ์„ค์น˜๋ฅผ ์œ„ํ•ด ํŒŒ์ด์ฌ ๋ฒ„์ „ 3.11๋กœ ๋‚ฎ์ถค
58
+ pip install torch --extra-index-url https://download.pytorch.org/whl/cpu
59
+
60
+ -------------------------------------------------------------------------
61
+ # 7. Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ&์‹คํ–‰
62
+ # docker build -t app .
63
+ # docker run -d -p 8000:8000 app
requirements.txt ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Web framework
2
+ fastapi[all]
3
+
4
+ # Database interaction
5
+ #sqlalchemy
6
+ #mysql-connector-python
7
+
8
+ # ASGI server
9
+ uvicorn
10
+
11
+ # File upload support
12
+ python-multipart
13
+
14
+ # Template engine
15
+ Jinja2
16
+
17
+ # LangChain for LLMs and AI workflow management
18
+ langchain
19
+
20
+ # LangChain HuggingFace integration
21
+ langchain-huggingface>=0.1.2
22
+
23
+ # Vector search and similarity
24
+ faiss-cpu
25
+
26
+ # OpenAI API (uncomment if needed)
27
+ #openai
28
+
29
+ # PDF processing
30
+ pypdf2
31
+ pypdf
32
+
33
+ # Community package for LangChain
34
+ langchain-community>=0.0.1
35
+
36
+ # Hugging Face integration
37
+ huggingface-hub>=0.23.0
38
+
39
+ protobuf
40
+
41
+ # Sentence Transformers for embeddings
42
+ sentence-transformers>=2.2.0, <4.0.0
43
+
44
+ # PyTorch installation from CPU-specific index
45
+ torch --extra-index-url https://download.pytorch.org/whl/cpu
46
+
47
+ # Gradio for building UI
48
+ #gradio
uploaded_files/a.txt ADDED
File without changes