Nikolay Angelov commited on
Commit
224b69c
ยท
1 Parent(s): f9764de

move everything to one app on port 7860 due to hugging face space limitations

Browse files
Files changed (8) hide show
  1. Dockerfile +4 -0
  2. README.md +21 -17
  3. UI.py +8 -6
  4. app.py +4 -14
  5. docker-compose.yml +0 -1
  6. main.py +9 -21
  7. requirements.txt +1 -3
  8. tools/web_search.py +0 -27
Dockerfile CHANGED
@@ -17,4 +17,8 @@ COPY --chown=user requirements.txt requirements.txt
17
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
18
 
19
  COPY --chown=user . /app
 
 
 
 
20
  CMD ["python", "main.py"]
 
17
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
18
 
19
  COPY --chown=user . /app
20
+
21
+ # Expose Gradio port (used by Hugging Face Spaces)
22
+ EXPOSE 7860
23
+
24
  CMD ["python", "main.py"]
README.md CHANGED
@@ -23,7 +23,7 @@ An AI-powered career coaching assistant built with FastAPI, Gradio UI, LangChain
23
 
24
  ## ๐Ÿš€ Features
25
 
26
- - **Dual Interface**: REST API (FastAPI) and Web UI (Gradio)
27
  - **AI-Powered Responses**: Utilizing Mixtral-8x7B-Instruct-v0.1 model
28
  - **Interactive Chat Interface**: Real-time conversation with the AI agent
29
  - **Multi-tool Integration**: Including webpage visits and time zone conversions
@@ -31,7 +31,7 @@ An AI-powered career coaching assistant built with FastAPI, Gradio UI, LangChain
31
 
32
  ## ๐Ÿ› ๏ธ Technical Stack
33
 
34
- - **Backend Framework**: FastAPI
35
  - **UI Framework**: Gradio with SmolaGents
36
  - **AI Framework**:
37
  - LangChain ReAct Agent (Backend) - For structured reasoning and tool usage
@@ -39,7 +39,6 @@ An AI-powered career coaching assistant built with FastAPI, Gradio UI, LangChain
39
  - **ML Models**: Hugging Face (Mixtral-8x7B-Instruct-v0.1)
40
  - **Additional Key Libraries**:
41
  - `uvicorn`: ASGI server
42
- - `accelerate`: ML model support
43
  - `markdownify`: Web content processing
44
  - `langchain`: AI framework and tools
45
  - `smolagents`: UI agent framework
@@ -78,16 +77,28 @@ export HUGGINGFACEHUB_API_TOKEN=your_token_here
78
  python main.py
79
  ```
80
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  ## ๐Ÿ“š API Documentation
82
 
83
- Once the server is running, access the API documentation at:
84
- - Swagger UI: http://localhost:8000/docs
85
- - ReDoc: http://localhost:8000/redoc
86
 
87
  ## ๐Ÿ”‘ Key Endpoints
88
 
89
- - `POST /agent/query`: Send queries to the AI agent
90
- - `GET /`: Redirects to Gradio UI
 
 
91
 
92
  ## ๐Ÿ” How It Works
93
 
@@ -98,19 +109,12 @@ The application uses a ReAct (Reasoning and Acting) agent pattern, which follows
98
  4. **Thought**: The agent reasons about the observation
99
  5. **Action**: The agent either uses another tool or provides a final answer
100
 
101
- This pattern allows the agent to:
102
- - Use tools in a structured way
103
- - Reason step-by-step about complex problems
104
- - Provide transparent decision-making
105
- - Handle multiple tool interactions
106
-
107
  ## โš ๏ธ Important Notes
108
 
109
  - The application requires active internet connection for AI model access
110
  - Hugging Face API token is required for model access
111
- - The application uses the Mixtral-8x7B-Instruct-v0.1 model for generating responses
112
- - The UI is built using SmolaGents framework for enhanced agent interactions
113
- - The backend uses LangChain's ReAct agent for structured reasoning and tool usage
114
 
115
  ## ๐Ÿค Contributing
116
 
 
23
 
24
  ## ๐Ÿš€ Features
25
 
26
+ - **Unified Interface**: Combined FastAPI and Gradio UI on a single port (7860)
27
  - **AI-Powered Responses**: Utilizing Mixtral-8x7B-Instruct-v0.1 model
28
  - **Interactive Chat Interface**: Real-time conversation with the AI agent
29
  - **Multi-tool Integration**: Including webpage visits and time zone conversions
 
31
 
32
  ## ๐Ÿ› ๏ธ Technical Stack
33
 
34
+ - **Backend Framework**: FastAPI (mounted with Gradio)
35
  - **UI Framework**: Gradio with SmolaGents
36
  - **AI Framework**:
37
  - LangChain ReAct Agent (Backend) - For structured reasoning and tool usage
 
39
  - **ML Models**: Hugging Face (Mixtral-8x7B-Instruct-v0.1)
40
  - **Additional Key Libraries**:
41
  - `uvicorn`: ASGI server
 
42
  - `markdownify`: Web content processing
43
  - `langchain`: AI framework and tools
44
  - `smolagents`: UI agent framework
 
77
  python main.py
78
  ```
79
 
80
+ The application will be available at:
81
+ - Main UI: http://localhost:7860
82
+ - API Documentation: http://localhost:7860/docs/
83
+
84
+ ## ๐ŸŒ Hugging Face Spaces Deployment
85
+
86
+ This application is specifically designed to work with Hugging Face Spaces:
87
+ - Uses a single port (7860) as required by Spaces
88
+ - Combines FastAPI and Gradio on the same port
89
+ - API documentation is accessible at `/docs/` on the same port
90
+ - All functionality works within Spaces' constraints
91
+
92
  ## ๐Ÿ“š API Documentation
93
 
94
+ The API documentation is available at `/docs/` on the same port as the main application (7860). This unified setup ensures compatibility with Hugging Face Spaces while maintaining all functionality.
 
 
95
 
96
  ## ๐Ÿ”‘ Key Endpoints
97
 
98
+ All endpoints are available on port 7860:
99
+ - `/`: Main Gradio UI
100
+ - `/docs/`: API Documentation
101
+ - `/agent/query`: Send queries to the AI agent
102
 
103
  ## ๐Ÿ” How It Works
104
 
 
109
  4. **Thought**: The agent reasons about the observation
110
  5. **Action**: The agent either uses another tool or provides a final answer
111
 
 
 
 
 
 
 
112
  ## โš ๏ธ Important Notes
113
 
114
  - The application requires active internet connection for AI model access
115
  - Hugging Face API token is required for model access
116
+ - All services run on port 7860 to comply with Hugging Face Spaces requirements
117
+ - The UI and API are served from the same port for better integration
 
118
 
119
  ## ๐Ÿค Contributing
120
 
UI.py CHANGED
@@ -1,7 +1,5 @@
1
- import mimetypes
2
  import os
3
  import re
4
- import shutil
5
  from typing import Optional, List, Dict, Any, Callable
6
 
7
  from smolagents.agent_types import AgentAudio, AgentImage, AgentText, handle_agent_output_types
@@ -208,8 +206,8 @@ class AgentUI:
208
  self.chat_history = []
209
  return "Started new conversation"
210
 
211
- def launch(self, server_name: str = "0.0.0.0", server_port: int = 7860, **kwargs):
212
- """Launch the Gradio interface"""
213
  api_port = int(self.api_url.split(":")[-1])
214
 
215
  with gr.Blocks(css="""
@@ -265,12 +263,16 @@ class AgentUI:
265
  outputs=[chatbot, menu_output]
266
  )
267
  docs_btn.click(
268
- fn=lambda: f"<script>window.open('{self.api_url.split(':')[0]}:8000/docs', '_blank')</script>",
269
  inputs=[],
270
  outputs=[menu_output]
271
  )
272
 
273
- # Launch the interface
 
 
 
 
274
  interface.launch(server_name=server_name, server_port=server_port, **kwargs)
275
 
276
  __all__ = ["stream_to_gradio", "AgentUI"]
 
 
1
  import os
2
  import re
 
3
  from typing import Optional, List, Dict, Any, Callable
4
 
5
  from smolagents.agent_types import AgentAudio, AgentImage, AgentText, handle_agent_output_types
 
206
  self.chat_history = []
207
  return "Started new conversation"
208
 
209
+ def get_gradio_app(self):
210
+ """Get the Gradio app for mounting in FastAPI"""
211
  api_port = int(self.api_url.split(":")[-1])
212
 
213
  with gr.Blocks(css="""
 
263
  outputs=[chatbot, menu_output]
264
  )
265
  docs_btn.click(
266
+ fn=lambda: f"<script>window.open('/docs', '_blank')</script>",
267
  inputs=[],
268
  outputs=[menu_output]
269
  )
270
 
271
+ return interface
272
+
273
+ def launch(self, server_name: str = "0.0.0.0", server_port: int = 7860, **kwargs):
274
+ """Launch the Gradio interface standalone (for development)"""
275
+ interface = self.get_gradio_app()
276
  interface.launch(server_name=server_name, server_port=server_port, **kwargs)
277
 
278
  __all__ = ["stream_to_gradio", "AgentUI"]
app.py CHANGED
@@ -4,6 +4,7 @@ from langchain.agents import AgentExecutor, create_react_agent
4
  from langchain_core.prompts import PromptTemplate
5
 
6
  from tools.visit_webpage import visit_webpage
 
7
 
8
  import gradio as gr
9
  import datetime
@@ -62,16 +63,6 @@ def get_current_time(timezone: str = "UTC") -> str:
62
  except Exception as e:
63
  return f"Error: {str(e)}"
64
 
65
- @tool
66
- def visit_webpage(url: str) -> str:
67
- """Visit a webpage and return its content as markdown."""
68
- try:
69
- response = requests.get(url, timeout=10)
70
- response.raise_for_status()
71
- return f"Successfully visited {url}. Content length: {len(response.text)} characters"
72
- except Exception as e:
73
- return f"Error visiting webpage: {str(e)}"
74
-
75
  # Load system prompt and template
76
  with open("prompts.yaml", 'r') as stream:
77
  prompt_templates = yaml.safe_load(stream)
@@ -80,7 +71,7 @@ with open("prompts.yaml", 'r') as stream:
80
  prompt = PromptTemplate.from_template(prompt_templates["template"])
81
 
82
  # Create the agent
83
- tools = [get_current_time, visit_webpage]
84
  agent = create_react_agent(
85
  llm=llm,
86
  tools=tools,
@@ -104,10 +95,9 @@ class QueryRequest(BaseModel):
104
  async def root():
105
  return HTMLResponse("<h2>Welcome! Please use the Gradio UI above.</h2>")
106
 
107
- @app.get("/docs")
108
  async def redirect_to_docs():
109
- base_url = app.url_path_for('root').replace('/docs', '')
110
- return RedirectResponse(url=f"{base_url}:8000/docs")
111
 
112
  @app.post("/agent/query")
113
  async def query_agent(request: QueryRequest):
 
4
  from langchain_core.prompts import PromptTemplate
5
 
6
  from tools.visit_webpage import visit_webpage
7
+ from tools.final_answer import final_answer
8
 
9
  import gradio as gr
10
  import datetime
 
63
  except Exception as e:
64
  return f"Error: {str(e)}"
65
 
 
 
 
 
 
 
 
 
 
 
66
  # Load system prompt and template
67
  with open("prompts.yaml", 'r') as stream:
68
  prompt_templates = yaml.safe_load(stream)
 
71
  prompt = PromptTemplate.from_template(prompt_templates["template"])
72
 
73
  # Create the agent
74
+ tools = [get_current_time]
75
  agent = create_react_agent(
76
  llm=llm,
77
  tools=tools,
 
95
  async def root():
96
  return HTMLResponse("<h2>Welcome! Please use the Gradio UI above.</h2>")
97
 
98
+ @app.get("/docs", include_in_schema=False)
99
  async def redirect_to_docs():
100
+ return RedirectResponse(url="/docs/")
 
101
 
102
  @app.post("/agent/query")
103
  async def query_agent(request: QueryRequest):
docker-compose.yml CHANGED
@@ -4,7 +4,6 @@ services:
4
  app:
5
  build: .
6
  ports:
7
- - "8000:8000"
8
  - "7860:7860"
9
  env_file:
10
  - .env
 
4
  app:
5
  build: .
6
  ports:
 
7
  - "7860:7860"
8
  env_file:
9
  - .env
main.py CHANGED
@@ -1,36 +1,24 @@
1
- import threading
2
- import time
3
  import uvicorn
4
  from app import app, agent
5
  from UI import AgentUI
6
 
7
  def main():
8
  """
9
- Run both FastAPI and Gradio UI together
10
  """
11
  # Configuration
12
- api_port = 8000
13
- ui_port = 7860
14
 
15
- # Start FastAPI in a background thread
16
- def run_fastapi():
17
- uvicorn.run(app, host="0.0.0.0", port=api_port)
18
-
19
- api_thread = threading.Thread(target=run_fastapi)
20
- api_thread.daemon = True
21
- api_thread.start()
22
-
23
- # Give FastAPI time to start
24
- print(f"Starting FastAPI server on port {api_port}...")
25
- time.sleep(1)
26
-
27
- # Start Gradio UI in the main thread
28
- print(f"Starting Gradio UI on port {ui_port}...")
29
  gradio_ui = AgentUI(
30
  agent=agent,
31
- api_url=f"http://localhost:{api_port}"
32
  )
33
- gradio_ui.launch(server_port=ui_port)
 
 
 
 
34
 
35
  if __name__ == "__main__":
36
  main()
 
 
 
1
  import uvicorn
2
  from app import app, agent
3
  from UI import AgentUI
4
 
5
  def main():
6
  """
7
+ Run FastAPI and Gradio UI on the same port
8
  """
9
  # Configuration
10
+ port = 7860
 
11
 
12
+ # Create and mount Gradio app
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  gradio_ui = AgentUI(
14
  agent=agent,
15
+ api_url=f"http://localhost:{port}"
16
  )
17
+ app.mount("/", gradio_ui.get_gradio_app())
18
+
19
+ # Start the combined app
20
+ print(f"Starting combined server on port {port}...")
21
+ uvicorn.run(app, host="0.0.0.0", port=port)
22
 
23
  if __name__ == "__main__":
24
  main()
requirements.txt CHANGED
@@ -4,7 +4,6 @@ requests>=2.31.0
4
  fastapi>=0.104.1
5
  uvicorn[standard]>=0.24.0
6
  gradio>=4.7.1
7
- accelerate>=0.26.0
8
  langchain>=0.1.0
9
  langchain-core>=0.1.0
10
  langchain-community>=0.0.13
@@ -13,5 +12,4 @@ pydantic>=2.5.2
13
  pytz>=2023.3
14
  python-dateutil>=2.8.2
15
  huggingface-hub>=0.19.4
16
- python-multipart>=0.0.6
17
- aiohttp>=3.9.0
 
4
  fastapi>=0.104.1
5
  uvicorn[standard]>=0.24.0
6
  gradio>=4.7.1
 
7
  langchain>=0.1.0
8
  langchain-core>=0.1.0
9
  langchain-community>=0.0.13
 
12
  pytz>=2023.3
13
  python-dateutil>=2.8.2
14
  huggingface-hub>=0.19.4
15
+ python-multipart>=0.0.6
 
tools/web_search.py DELETED
@@ -1,27 +0,0 @@
1
- from typing import Any, Optional
2
- from smolagents.tools import Tool
3
- import duckduckgo_search
4
-
5
- class DuckDuckGoSearchTool(Tool):
6
- name = "web_search"
7
- description = "Performs a duckduckgo web search based on your query (think a Google search) then returns the top search results."
8
- inputs = {'query': {'type': 'string', 'description': 'The search query to perform.'}}
9
- output_type = "string"
10
-
11
- def __init__(self, max_results=10, **kwargs):
12
- super().__init__()
13
- self.max_results = max_results
14
- try:
15
- from duckduckgo_search import DDGS
16
- except ImportError as e:
17
- raise ImportError(
18
- "You must install package `duckduckgo_search` to run this tool: for instance run `pip install duckduckgo-search`."
19
- ) from e
20
- self.ddgs = DDGS(**kwargs)
21
-
22
- def forward(self, query: str) -> str:
23
- results = self.ddgs.text(query, max_results=self.max_results)
24
- if len(results) == 0:
25
- raise Exception("No results found! Try a less restrictive/shorter query.")
26
- postprocessed_results = [f"[{result['title']}]({result['href']})\n{result['body']}" for result in results]
27
- return "## Search Results\n\n" + "\n\n".join(postprocessed_results)