ManTea commited on
Commit
db406df
·
1 Parent(s): 9bc0521
Files changed (6) hide show
  1. .dockerignore +88 -0
  2. .gitignore +157 -0
  3. DEPLOYMENT.md +47 -0
  4. Dockerfile +27 -0
  5. main.py +182 -0
  6. requirements.txt +3 -0
.dockerignore ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ env/
26
+ ENV/
27
+ .venv
28
+
29
+ # IDE
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # Environment files
37
+ .env
38
+ .env.*
39
+ !.env.example
40
+
41
+ # Logs
42
+ *.log
43
+ logs/
44
+ server.log
45
+
46
+ # Testing
47
+ .pytest_cache/
48
+ .coverage
49
+ htmlcov/
50
+ .tox/
51
+ .hypothesis/
52
+
53
+ # Database
54
+ *.db
55
+ *.sqlite
56
+ *.sqlite3
57
+
58
+ # Documentation
59
+ docs/_build/
60
+ *.md.backup
61
+
62
+ # Git
63
+ .git/
64
+ .gitignore
65
+ .gitattributes
66
+
67
+ # Docker
68
+ Dockerfile.*
69
+ docker-compose*.yml
70
+
71
+ # OS
72
+ .DS_Store
73
+ Thumbs.db
74
+
75
+ # Temporary files
76
+ *.tmp
77
+ *.temp
78
+ tmp/
79
+ temp/
80
+
81
+ # Migration files (if needed)
82
+ test_migration.py
83
+ test_mcp_tools.py
84
+ test_endpoints.sh
85
+ run_migration.sh
86
+ create_sample_users.py
87
+ *.pid
88
+
.gitignore ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =============================================================================
2
+ # PIXERSE LP CHATBOT - GIT IGNORE CONFIGURATION
3
+ # =============================================================================
4
+
5
+ # Environment files - NEVER commit these!
6
+ .env
7
+ .env.local
8
+ .env.development
9
+ .env.staging
10
+ .env.production
11
+
12
+ # Python
13
+ __pycache__/
14
+ *.py[cod]
15
+ *$py.class
16
+ *.so
17
+ .Python
18
+ build/
19
+ develop-eggs/
20
+ dist/
21
+ downloads/
22
+ eggs/
23
+ .eggs/
24
+ lib/
25
+ lib64/
26
+ parts/
27
+ sdist/
28
+ var/
29
+ wheels/
30
+ pip-wheel-metadata/
31
+ share/python-wheels/
32
+ *.egg-info/
33
+ .installed.cfg
34
+ *.egg
35
+ MANIFEST
36
+
37
+ # PyInstaller
38
+ *.manifest
39
+ *.spec
40
+
41
+ # Installer logs
42
+ pip-log.txt
43
+ pip-delete-this-directory.txt
44
+
45
+ # Unit test / coverage reports
46
+ htmlcov/
47
+ .tox/
48
+ .nox/
49
+ .coverage
50
+ .coverage.*
51
+ .cache
52
+ nosetests.xml
53
+ coverage.xml
54
+ *.cover
55
+ *.py,cover
56
+ .hypothesis/
57
+ .pytest_cache/
58
+
59
+ # Virtual environments
60
+ .env/
61
+ .venv/
62
+ env/
63
+ venv/
64
+ ENV/
65
+ env.bak/
66
+ venv.bak/
67
+
68
+ # Jupyter Notebook
69
+ .ipynb_checkpoints
70
+
71
+ # pyenv
72
+ .python-version
73
+
74
+ # pipenv
75
+ Pipfile.lock
76
+
77
+ # PEP 582
78
+ __pypackages__/
79
+
80
+ # Celery stuff
81
+ celerybeat-schedule
82
+ celerybeat.pid
83
+
84
+ # SageMath parsed files
85
+ *.sage.py
86
+
87
+ # Spyder project settings
88
+ .spyderproject
89
+ .spyproject
90
+
91
+ # Rope project settings
92
+ .ropeproject
93
+
94
+ # mkdocs documentation
95
+ /site
96
+
97
+ # mypy
98
+ .mypy_cache/
99
+ .dmypy.json
100
+ dmypy.json
101
+
102
+ # Pyre type checker
103
+ .pyre/
104
+
105
+ # IDEs
106
+ .vscode/
107
+ .idea/
108
+ *.swp
109
+ *.swo
110
+ *~
111
+
112
+ # macOS
113
+ .DS_Store
114
+ .AppleDouble
115
+ .LSOverride
116
+
117
+ # Windows
118
+ Thumbs.db
119
+ ehthumbs.db
120
+ Desktop.ini
121
+
122
+ # Logs
123
+ *.log
124
+ logs/
125
+
126
+ # Database
127
+ *.db
128
+ *.sqlite3
129
+
130
+ # Temporary files
131
+ *.tmp
132
+ *.temp
133
+ tmp/
134
+ temp/
135
+
136
+ # OS generated files
137
+ .DS_Store
138
+ .DS_Store?
139
+ ._*
140
+ .Spotlight-V100
141
+ .Trashes
142
+ ehthumbs.db
143
+ Thumbs.db
144
+
145
+ # Application specific
146
+ uploads/
147
+ downloads/
148
+ static/collected/
149
+
150
+ # Docker
151
+ .docker/
152
+
153
+ # Node.js (if any frontend components)
154
+ node_modules/
155
+ npm-debug.log*
156
+ yarn-debug.log*
157
+ yarn-error.log*
DEPLOYMENT.md ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hướng dẫn Deploy lên Hugging Face Spaces
2
+
3
+ ## Bước 1: Chuẩn bị Repository
4
+
5
+ 1. Tạo một Space mới trên Hugging Face: https://huggingface.co/new-space
6
+ 2. Chọn SDK: **Streamlit**
7
+ 3. Clone repository về máy hoặc upload files
8
+
9
+ ## Bước 2: Upload Files
10
+
11
+ Upload các files sau lên repository:
12
+
13
+ ```
14
+ ├── main.py # File chính của ứng dụng
15
+ ├── requirements.txt # Dependencies Python
16
+ ├── README.md # Metadata cho HF Spaces
17
+ ├── Dockerfile # Tùy chọn: để custom container
18
+ ├── .streamlit/
19
+ │ ├── config.toml # Cấu hình Streamlit
20
+ │ └── secrets.example.toml # Mẫu file secrets
21
+ ├── .env # Environment variables
22
+ └── .dockerignore # Ignore files cho Docker
23
+ ```
24
+
25
+ ## Bước 3: Cấu hình Environment Variables
26
+
27
+ Trong Hugging Face Spaces Settings, thêm:
28
+
29
+ - `API_BASE_URL`: URL của API backend (ví dụ: https://your-api.com)
30
+
31
+ ## Bước 4: Deploy
32
+
33
+ 1. Push code lên repository
34
+ 2. Hugging Face sẽ tự động build và deploy
35
+ 3. Kiểm tra logs nếu có lỗi
36
+
37
+ ## Lưu ý:
38
+
39
+ - Port mặc định cho HF Spaces: 7860
40
+ - App sẽ tự động restart khi có thay đổi code
41
+ - Có thể monitor logs trong tab "Logs" của Space
42
+
43
+ ## Troubleshooting:
44
+
45
+ 1. **API connection issues**: Kiểm tra `API_BASE_URL` trong settings
46
+ 2. **Import errors**: Kiểm tra `requirements.txt`
47
+ 3. **Port issues**: Đảm bảo app chạy trên port 7860
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies
6
+ RUN apt-get update && apt-get install -y \
7
+ build-essential \
8
+ curl \
9
+ software-properties-common \
10
+ git \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # Copy requirements and install Python dependencies
14
+ COPY requirements.txt .
15
+ RUN pip install --no-cache-dir -r requirements.txt
16
+
17
+ # Copy application files
18
+ COPY . .
19
+
20
+ # Expose port for Streamlit
21
+ EXPOSE 7860
22
+
23
+ # Health check
24
+ HEALTHCHECK CMD curl --fail http://localhost:7860/_stcore/health
25
+
26
+ # Run Streamlit app
27
+ ENTRYPOINT ["streamlit", "run", "main.py", "--server.port=7860", "--server.address=0.0.0.0"]
main.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import json
4
+ import uuid
5
+ from typing import Dict, Any
6
+ import os
7
+ from dotenv import load_dotenv
8
+
9
+ # Load environment variables
10
+ load_dotenv()
11
+
12
+ # Configuration
13
+ API_BASE_URL = os.getenv("API_BASE_URL", "https://pixerse-pixerse.hf.space") # Default to the env value
14
+ CHAT_ENDPOINT = f"{API_BASE_URL}/api/mcp/chat"
15
+
16
+ def call_chat_api(query: str, user_id: str) -> Dict[str, Any]:
17
+ """Call the FastAPI chat endpoint"""
18
+ payload = {
19
+ "query": query,
20
+ "user_id": user_id
21
+ }
22
+
23
+ try:
24
+ response = requests.post(
25
+ CHAT_ENDPOINT,
26
+ json=payload,
27
+ headers={"Content-Type": "application/json"},
28
+ timeout=30
29
+ )
30
+
31
+ if response.status_code == 200:
32
+ return {
33
+ "success": True,
34
+ "data": response.json()
35
+ }
36
+ else:
37
+ return {
38
+ "success": False,
39
+ "error": f"API Error: {response.status_code} - {response.text}"
40
+ }
41
+ except requests.exceptions.RequestException as e:
42
+ return {
43
+ "success": False,
44
+ "error": f"Connection Error: {str(e)}"
45
+ }
46
+
47
+ def main():
48
+ st.set_page_config(
49
+ page_title="PiXerse Chatbot",
50
+ page_icon="🤖",
51
+ layout="wide"
52
+ )
53
+
54
+ st.title("🤖 PiXerse AI Chatbot")
55
+ st.markdown("---")
56
+
57
+ # Initialize session state
58
+ if "messages" not in st.session_state:
59
+ st.session_state.messages = []
60
+
61
+ if "user_id" not in st.session_state:
62
+ st.session_state.user_id = str(uuid.uuid4())
63
+
64
+ # Sidebar for configuration
65
+ with st.sidebar:
66
+ st.header("⚙️ Configuration")
67
+
68
+ # Display user ID
69
+ st.text_input(
70
+ "User ID",
71
+ value=st.session_state.user_id,
72
+ disabled=True,
73
+ help="Your unique session identifier"
74
+ )
75
+
76
+ # API Status check
77
+ st.subheader("🔍 API Status")
78
+ if st.button("Check API Status"):
79
+ try:
80
+ health_response = requests.get(f"{API_BASE_URL}/docs", timeout=5)
81
+ if health_response.status_code == 200:
82
+ st.success("✅ API is running")
83
+ else:
84
+ st.error("❌ API is not responding properly")
85
+ except:
86
+ st.error("❌ Cannot connect to API")
87
+
88
+ # Clear chat button
89
+ if st.button("🗑️ Clear Chat History"):
90
+ st.session_state.messages = []
91
+ st.rerun()
92
+
93
+ # Main chat interface
94
+ st.subheader("💬 Chat Interface")
95
+
96
+ # Display chat messages
97
+ chat_container = st.container()
98
+ with chat_container:
99
+ for message in st.session_state.messages:
100
+ with st.chat_message(message["role"]):
101
+ st.markdown(message["content"])
102
+
103
+ # Show additional info for assistant messages
104
+ if message["role"] == "assistant" and "metadata" in message:
105
+ with st.expander("📊 Response Details"):
106
+ metadata = message["metadata"]
107
+ col_meta1, col_meta2 = st.columns(2)
108
+
109
+ with col_meta1:
110
+ st.metric("Token Usage", metadata.get("token_usage", "N/A"))
111
+
112
+ with col_meta2:
113
+ st.metric("Tools Used", len(metadata.get("tools_used", [])))
114
+
115
+ if metadata.get("tools_used"):
116
+ st.write("**Tools Used:**")
117
+ for tool in metadata["tools_used"]:
118
+ st.code(tool, language="text")
119
+
120
+ if metadata.get("tools_response"):
121
+ st.write("**Tool Responses:**")
122
+ for i, response in enumerate(metadata["tools_response"]):
123
+ st.json(response)
124
+
125
+ # Chat input (outside columns to avoid Streamlit API error)
126
+ if prompt := st.chat_input("Type your message here..."):
127
+ # Add user message to chat
128
+ st.session_state.messages.append({
129
+ "role": "user",
130
+ "content": prompt
131
+ })
132
+
133
+ # Rerun to display new message immediately
134
+ st.rerun()
135
+
136
+ # Process last message if it's from user and no assistant response yet
137
+ if (st.session_state.messages and
138
+ st.session_state.messages[-1]["role"] == "user" and
139
+ (len(st.session_state.messages) == 1 or
140
+ st.session_state.messages[-2]["role"] == "assistant")):
141
+
142
+ user_message = st.session_state.messages[-1]["content"]
143
+
144
+ # Show processing message
145
+ with st.chat_message("assistant"):
146
+ with st.spinner("🤔 Thinking..."):
147
+ response = call_chat_api(user_message, st.session_state.user_id)
148
+
149
+ if response["success"]:
150
+ data = response["data"]
151
+ assistant_message = data["response"]
152
+
153
+ # Display response
154
+ st.markdown(assistant_message)
155
+
156
+ # Add assistant message to chat with metadata
157
+ st.session_state.messages.append({
158
+ "role": "assistant",
159
+ "content": assistant_message,
160
+ "metadata": {
161
+ "token_usage": data.get("token_usage", 0),
162
+ "tools_used": data.get("tools_used", []),
163
+ "tools_response": data.get("tools_response", [])
164
+ }
165
+ })
166
+
167
+ else:
168
+ error_message = f"❌ Error: {response['error']}"
169
+ st.error(error_message)
170
+
171
+ # Add error message to chat
172
+ st.session_state.messages.append({
173
+ "role": "assistant",
174
+ "content": error_message
175
+ })
176
+
177
+ # Rerun to update the display
178
+ st.rerun()
179
+
180
+ if __name__ == "__main__":
181
+ # Run Streamlit UI
182
+ main()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ streamlit>=1.28.0
2
+ requests>=2.31.0
3
+ python-dotenv>=1.0.0