File size: 3,510 Bytes
00c776f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122fef6
 
 
 
00c776f
 
122fef6
 
 
 
00c776f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122fef6
00c776f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122fef6
00c776f
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# app.py β€” Entry point for HF Spaces
import os
import gradio as gr
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langchain_groq import ChatGroq
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.tools import tool
import requests

os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY")

# ─── LLM ─────────────────────────────────
llm = ChatGroq(
    model="llama-3.1-8b-instant",
    max_tokens=300,
    temperature=0
)

# ─── Tools ───────────────────────────────
@tool
def search(query: str) -> str:
    """Search the web for current information."""
    return DuckDuckGoSearchRun().run(query)

@tool
def calculator(first_num: float, second_num: float, operation: str) -> dict:
    """Perform basic arithmetic. Operations: add, sub, mul, div"""
    ops = {"add": first_num + second_num, "sub": first_num - second_num,
           "mul": first_num * second_num}
    if operation == "div":
        return {"result": "Division by zero" if second_num == 0 else first_num / second_num}
    return {"result": ops.get(operation, f"Unknown operation: {operation}")}

@tool
def get_stock_price(symbol: str) -> dict:
    """Fetch latest stock price for a symbol like AAPL or TSLA."""
    url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={symbol}&apikey={os.getenv('STOCKS_API_KEY')}"
    return requests.get(url).json()

tools = [search, calculator, get_stock_price]
llm_with_tools = llm.bind_tools(tools)

# ─── State ───────────────────────────────
class ChatState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]

# ─── Graph ───────────────────────────────
def chat_node(state: ChatState):
    """LLM node that may answer or request a tool call."""
    messages = state['messages']
    response = llm_with_tools.invoke(messages)
    return {"messages": [response]}

graph = StateGraph(ChatState)
graph.add_node("chat_node", chat_node)
graph.add_node("tools", ToolNode(tools))
graph.add_edge(START, "chat_node")
graph.add_conditional_edges("chat_node", tools_condition)
graph.add_edge("tools", "chat_node")
agent = graph.compile()

# ─── Gradio UI ───────────────────────────
def respond(message, history):
    # Convert Gradio history β†’ LangChain messages
    messages = []
    for user_msg, bot_msg in history:
        messages.append(HumanMessage(content=user_msg))
        if bot_msg:
            messages.append(AIMessage(content=bot_msg))
    messages.append(HumanMessage(content=message))

    result = agent.invoke({"messages": messages})
    return result["messages"][-1].content

demo = gr.ChatInterface(
    fn=respond,
    title="πŸ” AI Research Agent",
    description="Ask me anything β€” I can search the web and do calculations!",
    examples=[
        "What is LangGraph?",
        "What's happening in AI news today?",
        "Calculate 128 multiplied by 37",
    ],
    cache_examples=False   # πŸ”₯ ADD THIS
)

if __name__ == "__main__":
    demo.launch()