T-K-O-H commited on
Commit
d4318c3
·
1 Parent(s): 4862e4c

Update README

Browse files
Files changed (7) hide show
  1. Dockerfile +0 -23
  2. README.md +17 -0
  3. app.py +0 -198
  4. index.html +0 -40
  5. requirements.txt +0 -7
  6. static/script.js +0 -128
  7. static/styles.css +0 -155
Dockerfile DELETED
@@ -1,23 +0,0 @@
1
- FROM python:3.10-slim
2
-
3
- WORKDIR /app
4
-
5
- # Copy requirements and install dependencies
6
- COPY requirements.txt .
7
- RUN pip install --no-cache-dir -r requirements.txt
8
-
9
- # Copy the rest of the code
10
- COPY . .
11
-
12
- # Create necessary directories if they don't exist
13
- RUN mkdir -p static
14
-
15
- # Make port 7860 available (Hugging Face Spaces uses this port)
16
- EXPOSE 7860
17
-
18
- # Set environment variables
19
- ENV HOST=0.0.0.0
20
- ENV PORT=7860
21
-
22
- # Run the application
23
- CMD ["python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,3 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # LangGraph Agent with Tools
2
 
3
  This project demonstrates a conversational AI agent built with LangGraph, featuring three integrated tools and a responsive web UI.
 
1
+ ---
2
+ title: WebAgentTools
3
+ emoji: 🛠️
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: "4.19.2"
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # WebAgentTools
13
+
14
+ A web-based agent tools interface for interacting with various AI tools and services.
15
+
16
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
17
+
18
  # LangGraph Agent with Tools
19
 
20
  This project demonstrates a conversational AI agent built with LangGraph, featuring three integrated tools and a responsive web UI.
app.py DELETED
@@ -1,198 +0,0 @@
1
- # app.py
2
- from typing import Annotated, Any, Dict, List, Literal, Sequence, TypedDict, Union
3
- import os
4
- import asyncio
5
- from operator import itemgetter
6
- from dotenv import load_dotenv
7
- from fastapi import FastAPI, WebSocket, WebSocketDisconnect
8
- from fastapi.staticfiles import StaticFiles
9
- from fastapi.responses import FileResponse
10
- import uvicorn
11
-
12
- # LangGraph
13
- from langgraph.graph import END, StateGraph
14
- from langgraph.prebuilt import ToolExecutor
15
- from langgraph.prebuilt.tool_node import ToolNode
16
-
17
- # LangChain
18
- from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
19
- from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
20
- from langchain_core.output_parsers import JsonOutputParser
21
- from langchain_core.tools import tool
22
- from langchain_openai import ChatOpenAI
23
-
24
- # Load environment variables from .env file
25
- load_dotenv()
26
-
27
- # Setup FastAPI app
28
- app = FastAPI()
29
-
30
-
31
- # Define the agent's state
32
- class AgentState(TypedDict):
33
- messages: Sequence[Union[AIMessage, HumanMessage, SystemMessage, ToolMessage]]
34
- tools: List[Dict]
35
-
36
-
37
- # Define tools
38
- @tool
39
- def search_web(query: str) -> str:
40
- """Search the web for information on a given query."""
41
- # In a real implementation, this would connect to a search API
42
- return f"Found information about {query}: This is a simulated web search result for '{query}'."
43
-
44
-
45
- @tool
46
- def calculate(expression: str) -> str:
47
- """Calculate the result of a mathematical expression."""
48
- try:
49
- # CAUTION: eval can be dangerous in production; consider using a safer alternative
50
- return f"Result: {eval(expression)}"
51
- except Exception as e:
52
- return f"Error calculating: {str(e)}"
53
-
54
-
55
- @tool
56
- def generate_image_prompt(description: str) -> str:
57
- """Generate a detailed prompt for image generation based on a description."""
58
- enhanced_prompt = f"An image of {description}, high resolution, detailed lighting, professional quality"
59
- return f"Generated image prompt: {enhanced_prompt}"
60
-
61
-
62
- # Set up the language model
63
- model = ChatOpenAI(temperature=0.5)
64
-
65
- # Create tools list
66
- tools = [search_web, calculate, generate_image_prompt]
67
- tool_executor = ToolExecutor(tools)
68
-
69
-
70
- # Define agent nodes
71
- def create_agent_node():
72
- # System prompt for the agent
73
- system_prompt = """You are a helpful AI assistant with access to the following tools:
74
-
75
- 1. search_web: Search the web for information
76
- 2. calculate: Calculate the result of mathematical expressions
77
- 3. generate_image_prompt: Generate prompts for image generation
78
-
79
- Use these tools to assist the user with their requests. When a tool is needed, call the appropriate function.
80
- """
81
-
82
- # Create the prompt template
83
- prompt = ChatPromptTemplate.from_messages(
84
- [
85
- SystemMessage(content=system_prompt),
86
- MessagesPlaceholder(variable_name="messages"),
87
- ]
88
- )
89
-
90
- # Create the agent
91
- return prompt | model.bind_tools(tools=tools)
92
-
93
-
94
- # Define routing logic
95
- def should_continue(state: AgentState) -> Literal["continue", "end"]:
96
- """Determine if the agent should continue or end."""
97
- # Get the last message
98
- last_message = state["messages"][-1]
99
-
100
- # If the last message is from the AI and doesn't call a tool, we're done
101
- if isinstance(last_message, AIMessage) and not last_message.tool_calls:
102
- return "end"
103
- return "continue"
104
-
105
-
106
- def which_tool(state: AgentState) -> str:
107
- """Determine which tool to use."""
108
- last_message = state["messages"][-1]
109
- if not isinstance(last_message, AIMessage):
110
- raise ValueError("Expected AIMessage")
111
- if not last_message.tool_calls:
112
- raise ValueError("Expected tool_calls")
113
- # Just return the name of the first tool
114
- return last_message.tool_calls[0]["name"]
115
-
116
-
117
- # Create the graph
118
- def build_agent_graph():
119
- """Build the LangGraph for the agent."""
120
- # Create the graph
121
- graph = StateGraph(AgentState)
122
-
123
- # Add the nodes
124
- graph.add_node("agent", create_agent_node())
125
- graph.add_node("tool", ToolNode(tool_executor))
126
-
127
- # Add the edges
128
- graph.add_conditional_edges(
129
- "agent",
130
- should_continue,
131
- {
132
- "continue": "tool",
133
- "end": END,
134
- },
135
- )
136
- graph.add_conditional_edges(
137
- "tool",
138
- lambda state: "agent",
139
- {
140
- "agent": "agent",
141
- },
142
- )
143
-
144
- # Set the entry point
145
- graph.set_entry_point("agent")
146
-
147
- return graph.compile()
148
-
149
-
150
- # Create the agent executor
151
- agent_executor = build_agent_graph()
152
-
153
-
154
- # WebSocket for real-time communication
155
- @app.websocket("/ws")
156
- async def websocket_endpoint(websocket: WebSocket):
157
- await websocket.accept()
158
- try:
159
- # Initialize the state
160
- state = {
161
- "messages": [],
162
- "tools": tools,
163
- }
164
-
165
- while True:
166
- data = await websocket.receive_text()
167
- # Add the user message to the state
168
- state["messages"].append(HumanMessage(content=data))
169
-
170
- # Run the agent
171
- result = agent_executor.invoke(state)
172
-
173
- # Get the final AI message
174
- for message in result["messages"]:
175
- if isinstance(message, AIMessage):
176
- await websocket.send_json({"type": "ai_message", "content": message.content})
177
- elif isinstance(message, ToolMessage):
178
- await websocket.send_json({"type": "tool_message", "content": message.content})
179
-
180
- # Update the state
181
- state = result
182
-
183
- except WebSocketDisconnect:
184
- print("Client disconnected")
185
-
186
-
187
- # Serve the HTML frontend
188
- @app.get("/")
189
- async def get():
190
- return FileResponse("index.html")
191
-
192
-
193
- # Mount static files
194
- app.mount("/static", StaticFiles(directory="static"), name="static")
195
-
196
-
197
- if __name__ == "__main__":
198
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
index.html DELETED
@@ -1,40 +0,0 @@
1
- <!-- index.html -->
2
- <!DOCTYPE html>
3
- <html lang="en">
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>LangGraph Agent</title>
8
- <link rel="stylesheet" href="static/styles.css">
9
- </head>
10
- <body>
11
- <div class="container">
12
- <header>
13
- <h1>LangGraph Agent with Tools</h1>
14
- <p>Ask me anything! I can search the web, calculate expressions, and generate image prompts.</p>
15
- </header>
16
-
17
- <div class="chat-container">
18
- <div id="chat-messages" class="chat-messages"></div>
19
-
20
- <div class="message-input">
21
- <form id="message-form">
22
- <input type="text" id="user-input" placeholder="Type your message here..." autocomplete="off">
23
- <button type="submit">Send</button>
24
- </form>
25
- </div>
26
- </div>
27
-
28
- <div class="tools-info">
29
- <h3>Available Tools:</h3>
30
- <ul>
31
- <li><strong>Search Web</strong> - Find information online</li>
32
- <li><strong>Calculate</strong> - Solve mathematical expressions</li>
33
- <li><strong>Generate Image Prompt</strong> - Create detailed image generation prompts</li>
34
- </ul>
35
- </div>
36
- </div>
37
-
38
- <script src="static/script.js"></script>
39
- </body>
40
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt DELETED
@@ -1,7 +0,0 @@
1
- fastapi==0.109.0
2
- uvicorn==0.27.0
3
- python-dotenv==1.0.0
4
- langgraph==0.1.10
5
- langchain-core==0.1.17
6
- langchain-openai==0.0.5
7
- websockets==12.0
 
 
 
 
 
 
 
 
static/script.js DELETED
@@ -1,128 +0,0 @@
1
- // static/script.js
2
- document.addEventListener('DOMContentLoaded', function() {
3
- const messageForm = document.getElementById('message-form');
4
- const userInput = document.getElementById('user-input');
5
- const chatMessages = document.getElementById('chat-messages');
6
- let websocket;
7
-
8
- // Add welcome message
9
- addMessage('Welcome! I can help you search the web, calculate expressions, and generate image prompts. What would you like to do?', 'ai');
10
-
11
- // Connect WebSocket
12
- function connectWebSocket() {
13
- // Get the correct WebSocket URL based on the current page URL
14
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
15
- const wsUrl = `${protocol}//${window.location.host}/ws`;
16
-
17
- websocket = new WebSocket(wsUrl);
18
-
19
- websocket.onopen = function(event) {
20
- console.log('WebSocket connected');
21
- };
22
-
23
- websocket.onmessage = function(event) {
24
- const data = JSON.parse(event.data);
25
-
26
- if (data.type === 'ai_message') {
27
- addMessage(data.content, 'ai');
28
- } else if (data.type === 'tool_message') {
29
- addMessage(data.content, 'tool');
30
- }
31
- };
32
-
33
- websocket.onclose = function(event) {
34
- console.log('WebSocket disconnected');
35
- // Try to reconnect after a delay
36
- setTimeout(connectWebSocket, 3000);
37
- };
38
-
39
- websocket.onerror = function(error) {
40
- console.error('WebSocket error:', error);
41
- };
42
- }
43
-
44
- // Connect to WebSocket when page loads
45
- connectWebSocket();
46
-
47
- // Handle message submission
48
- messageForm.addEventListener('submit', function(event) {
49
- event.preventDefault();
50
- const message = userInput.value.trim();
51
-
52
- if (message) {
53
- // Add user message to chat
54
- addMessage(message, 'user');
55
-
56
- // Send message to server via WebSocket
57
- if (websocket && websocket.readyState === WebSocket.OPEN) {
58
- websocket.send(message);
59
-
60
- // Add a "thinking" message
61
- const thinkingId = addThinkingMessage();
62
-
63
- // Clear input
64
- userInput.value = '';
65
-
66
- // Focus on input
67
- userInput.focus();
68
- } else {
69
- addMessage('Connection lost. Trying to reconnect...', 'system');
70
- connectWebSocket();
71
- }
72
- }
73
- });
74
-
75
- // Function to add a message to the chat
76
- function addMessage(content, type) {
77
- // Remove any thinking message first
78
- removeThinkingMessage();
79
-
80
- const messageDiv = document.createElement('div');
81
- messageDiv.classList.add('message');
82
-
83
- switch (type) {
84
- case 'user':
85
- messageDiv.classList.add('user-message');
86
- break;
87
- case 'ai':
88
- messageDiv.classList.add('ai-message');
89
- break;
90
- case 'tool':
91
- messageDiv.classList.add('tool-message');
92
- break;
93
- default:
94
- messageDiv.classList.add('system-message');
95
- }
96
-
97
- // Process markdown-like code blocks
98
- content = content.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
99
-
100
- messageDiv.innerHTML = content;
101
- chatMessages.appendChild(messageDiv);
102
-
103
- // Scroll to bottom
104
- chatMessages.scrollTop = chatMessages.scrollHeight;
105
- }
106
-
107
- // Function to add a "thinking" message
108
- function addThinkingMessage() {
109
- const thinkingDiv = document.createElement('div');
110
- thinkingDiv.classList.add('message', 'ai-message', 'thinking');
111
- thinkingDiv.id = 'thinking-message';
112
- thinkingDiv.textContent = 'Thinking...';
113
- chatMessages.appendChild(thinkingDiv);
114
-
115
- // Scroll to bottom
116
- chatMessages.scrollTop = chatMessages.scrollHeight;
117
-
118
- return 'thinking-message';
119
- }
120
-
121
- // Function to remove the "thinking" message
122
- function removeThinkingMessage() {
123
- const thinkingMessage = document.getElementById('thinking-message');
124
- if (thinkingMessage) {
125
- thinkingMessage.remove();
126
- }
127
- }
128
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/styles.css DELETED
@@ -1,155 +0,0 @@
1
- /* static/styles.css */
2
- * {
3
- box-sizing: border-box;
4
- margin: 0;
5
- padding: 0;
6
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
7
- }
8
-
9
- body {
10
- background-color: #f5f5f5;
11
- color: #333;
12
- line-height: 1.6;
13
- }
14
-
15
- .container {
16
- max-width: 900px;
17
- margin: 0 auto;
18
- padding: 20px;
19
- }
20
-
21
- header {
22
- text-align: center;
23
- margin-bottom: 30px;
24
- padding: 20px;
25
- background-color: #ffffff;
26
- border-radius: 10px;
27
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
28
- }
29
-
30
- header h1 {
31
- color: #2c3e50;
32
- margin-bottom: 10px;
33
- }
34
-
35
- .chat-container {
36
- background-color: #ffffff;
37
- border-radius: 10px;
38
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
39
- overflow: hidden;
40
- margin-bottom: 20px;
41
- }
42
-
43
- .chat-messages {
44
- height: 400px;
45
- padding: 20px;
46
- overflow-y: auto;
47
- }
48
-
49
- .message {
50
- margin-bottom: 15px;
51
- padding: 10px 15px;
52
- border-radius: 18px;
53
- max-width: 80%;
54
- word-wrap: break-word;
55
- }
56
-
57
- .user-message {
58
- background-color: #e9f3ff;
59
- color: #333;
60
- margin-left: auto;
61
- border-bottom-right-radius: 5px;
62
- }
63
-
64
- .ai-message {
65
- background-color: #f0f0f0;
66
- color: #333;
67
- margin-right: auto;
68
- border-bottom-left-radius: 5px;
69
- }
70
-
71
- .tool-message {
72
- background-color: #fcf8e3;
73
- color: #8a6d3b;
74
- margin-right: auto;
75
- border-bottom-left-radius: 5px;
76
- font-style: italic;
77
- }
78
-
79
- .message-input {
80
- display: flex;
81
- padding: 15px;
82
- background-color: #f9f9f9;
83
- border-top: 1px solid #eaeaea;
84
- }
85
-
86
- #message-form {
87
- display: flex;
88
- width: 100%;
89
- }
90
-
91
- #user-input {
92
- flex-grow: 1;
93
- padding: 12px;
94
- border: 1px solid #ddd;
95
- border-radius: 20px;
96
- outline: none;
97
- font-size: 16px;
98
- }
99
-
100
- button {
101
- padding: 0 20px;
102
- background-color: #3498db;
103
- color: white;
104
- border: none;
105
- border-radius: 20px;
106
- margin-left: 10px;
107
- cursor: pointer;
108
- font-size: 16px;
109
- transition: background-color 0.3s;
110
- }
111
-
112
- button:hover {
113
- background-color: #2980b9;
114
- }
115
-
116
- .tools-info {
117
- background-color: #ffffff;
118
- border-radius: 10px;
119
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
120
- padding: 20px;
121
- }
122
-
123
- .tools-info h3 {
124
- margin-bottom: 10px;
125
- color: #2c3e50;
126
- }
127
-
128
- .tools-info ul {
129
- list-style-type: none;
130
- }
131
-
132
- .tools-info li {
133
- margin-bottom: 8px;
134
- padding-left: 20px;
135
- position: relative;
136
- }
137
-
138
- .tools-info li::before {
139
- content: "•";
140
- position: absolute;
141
- left: 0;
142
- color: #3498db;
143
- }
144
-
145
- code {
146
- background-color: #f8f8f8;
147
- padding: 2px 4px;
148
- border-radius: 4px;
149
- font-family: 'Courier New', monospace;
150
- }
151
-
152
- .thinking {
153
- font-style: italic;
154
- color: #888;
155
- }