PuruAI commited on
Commit
1921e84
·
verified ·
1 Parent(s): 4c18251

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +218 -4
app.py CHANGED
@@ -9,19 +9,233 @@ from fastapi import FastAPI, Depends, HTTPException
9
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
10
  import uvicorn
11
 
12
- # Transformers pipeline for LLM
13
  from transformers import pipeline
14
 
15
- # ===== LangChain Community Imports (v0.3+) =====
16
  from langchain_community.llms import HuggingFacePipeline
17
  from langchain_community.utilities import SerpAPIWrapper
18
  from langchain_community.vectorstores import Chroma
19
  from langchain_community.embeddings import HuggingFaceEmbeddings
20
- from langchain_community.tools import PythonREPL
21
 
22
- # ===== LangChain Core Imports =====
23
  from langchain.memory import ConversationBufferMemory
24
  from langchain.agents import initialize_agent, Tool
25
  from langchain.chains import LLMChain
26
  from langchain.prompts import PromptTemplate
27
  from langchain.docstore.document import Document
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
10
  import uvicorn
11
 
 
12
  from transformers import pipeline
13
 
14
+ # ===== LangChain imports (v0.3.x + community modules) =====
15
  from langchain_community.llms import HuggingFacePipeline
16
  from langchain_community.utilities import SerpAPIWrapper
17
  from langchain_community.vectorstores import Chroma
18
  from langchain_community.embeddings import HuggingFaceEmbeddings
 
19
 
 
20
  from langchain.memory import ConversationBufferMemory
21
  from langchain.agents import initialize_agent, Tool
22
  from langchain.chains import LLMChain
23
  from langchain.prompts import PromptTemplate
24
  from langchain.docstore.document import Document
25
+ from langchain.tools.python.tool import PythonAstREPLTool # ✅ Updated Python REPL
26
+
27
+ # ===========================================
28
+ # ENVIRONMENT VARIABLES
29
+ # ===========================================
30
+ HF_TOKEN = os.getenv("HF_TOKEN")
31
+ SERPAPI_KEY = os.getenv("SERPAPI_API_KEY")
32
+ JWT_SECRET = os.getenv("JWT_SECRET", "changeme123")
33
+
34
+ # ===========================================
35
+ # AUTH
36
+ # ===========================================
37
+ security = HTTPBearer()
38
+
39
+ def verify_jwt(credentials: HTTPAuthorizationCredentials = Depends(security)):
40
+ token = credentials.credentials
41
+ if token != JWT_SECRET:
42
+ raise HTTPException(status_code=403, detail="Invalid token")
43
+ return True
44
+
45
+ # ===========================================
46
+ # MODEL LOADER
47
+ # ===========================================
48
+ MODEL_ID = "PuruAI/Medini_Intelligence"
49
+ FALLBACK_MODEL = "gpt2"
50
+
51
+ def load_llm():
52
+ pipeline_kwargs = {"max_new_tokens": 512, "temperature": 0.7}
53
+ try:
54
+ model_pipeline = pipeline("text-generation", model=MODEL_ID, use_auth_token=HF_TOKEN, **pipeline_kwargs)
55
+ except Exception:
56
+ print(f"Warning: Failed to load {MODEL_ID}. Falling back to {FALLBACK_MODEL}.")
57
+ model_pipeline = pipeline("text-generation", model=FALLBACK_MODEL, **pipeline_kwargs)
58
+ return HuggingFacePipeline(pipeline=model_pipeline)
59
+
60
+ llm = load_llm()
61
+
62
+ # ===========================================
63
+ # VECTOR MEMORY
64
+ # ===========================================
65
+ embeddings = HuggingFaceEmbeddings()
66
+ chroma_db = Chroma(persist_directory="./medini_memory", embedding_function=embeddings)
67
+ retriever = chroma_db.as_retriever()
68
+
69
+ qa_prompt_template = """
70
+ You are a question-answering system. Use the following context, which contains information retrieved from memory, to answer the user's question.
71
+ If the context is empty or does not contain the answer, state clearly that the information is not in memory.
72
+ Context:
73
+ {context}
74
+ Question: {question}
75
+ Answer:
76
+ """
77
+ QA_PROMPT = PromptTemplate(template=qa_prompt_template, input_variables=["context", "question"])
78
+ qa_chain = LLMChain(llm=llm, prompt=QA_PROMPT)
79
+
80
+ def retrieve_and_answer(question: str) -> str:
81
+ docs = retriever.get_relevant_documents(question)
82
+ context = "\n---\n".join([d.page_content for d in docs])
83
+ return qa_chain.run(context=context, question=question)
84
+
85
+ # ===========================================
86
+ # TOOLS
87
+ # ===========================================
88
+ search = SerpAPIWrapper(serpapi_api_key=SERPAPI_KEY)
89
+ python_tool = PythonAstREPLTool() # ✅ Correct REPL
90
+
91
+ tools = [
92
+ Tool(
93
+ name="Knowledge Recall",
94
+ func=retrieve_and_answer,
95
+ description="Retrieve info from Medini memory (Chroma DB)."
96
+ ),
97
+ Tool(
98
+ name="Web Search",
99
+ func=search.run,
100
+ description="Search the web for up-to-date information."
101
+ ),
102
+ Tool(
103
+ name="Python REPL",
104
+ func=python_tool.run,
105
+ description="Execute Python code, useful for math and data manipulation."
106
+ ),
107
+ ]
108
+
109
+ TOOL_MAP = {tool.name.lower().replace(" ", ""): tool.func for tool in tools}
110
+
111
+ # ===========================================
112
+ # AGENT
113
+ # ===========================================
114
+ memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
115
+ agent = initialize_agent(
116
+ tools=tools,
117
+ llm=llm,
118
+ agent="conversational-react-description",
119
+ memory=memory,
120
+ verbose=True
121
+ )
122
+
123
+ # ===========================================
124
+ # PLANNER
125
+ # ===========================================
126
+ plan_prompt = PromptTemplate(
127
+ input_variables=["goal"],
128
+ template="""
129
+ You are Medini Planner. Decompose the high-level goal into a JSON object containing a 'steps' array (max 6 steps). Each step must have: id, name, description, tool_hint (recall, search, python, agent).
130
+ Return JSON only.
131
+ Goal: {goal}
132
+ """
133
+ )
134
+ planner_chain = LLMChain(llm=llm, prompt=plan_prompt)
135
+
136
+ def create_plan(goal: str) -> Dict[str, Any]:
137
+ raw = planner_chain.run(goal=goal)
138
+ m = re.search(r"\{.*\}", raw, flags=re.DOTALL)
139
+ json_str = m.group(0) if m else raw
140
+ json_str = json_str.replace("```json", "").replace("```", "").strip()
141
+ try:
142
+ plan = json.loads(json_str)
143
+ if 'steps' not in plan:
144
+ raise ValueError("Parsed JSON is missing 'steps'")
145
+ return plan
146
+ except json.JSONDecodeError as e:
147
+ print(f"JSON Parsing Error: {e} in string: {json_str[:200]}...")
148
+ raise ValueError("Planner returned malformed JSON.") from e
149
+
150
+ def execute_step(step: Dict[str, Any]) -> Dict[str, Any]:
151
+ hint = (step.get("tool_hint") or "").lower()
152
+ input_text = step.get("description")
153
+ output, status = "Execution skipped.", "error"
154
+ try:
155
+ tool_func = None
156
+ if "recall" in hint:
157
+ tool_func = TOOL_MAP.get("knowledgerecall")
158
+ elif "search" in hint:
159
+ tool_func = TOOL_MAP.get("websearch")
160
+ elif "python" in hint:
161
+ tool_func = TOOL_MAP.get("pythonrepl")
162
+ if tool_func:
163
+ output = tool_func(input_text)
164
+ else:
165
+ output = agent.run(input_text)
166
+ status = "ok"
167
+ except Exception as e:
168
+ output = f"Execution Error: {str(e)}"
169
+ chroma_db.add_documents([Document(page_content=f"Step {step['id']} - {step['name']} Result: {output}")])
170
+ return {"id": step['id'], "name": step['name'], "status": status, "output": output}
171
+
172
+ def execute_plan(goal: str) -> Dict[str, Any]:
173
+ try:
174
+ plan = create_plan(goal)
175
+ except ValueError as e:
176
+ return {"goal": goal, "error": str(e)}
177
+ results = [execute_step(step) for step in plan.get("steps", [])]
178
+ return {"goal": goal, "plan": plan, "results": results}
179
+
180
+ # ===========================================
181
+ # FASTAPI BACKEND
182
+ # ===========================================
183
+ app = FastAPI(title="Medini Agent API")
184
+
185
+ @app.post("/chat")
186
+ def chat_endpoint(message: str, auth: bool = Depends(verify_jwt)):
187
+ response = agent.run(message)
188
+ return {"response": response}
189
+
190
+ @app.post("/goal")
191
+ def goal_endpoint(goal: str, auth: bool = Depends(verify_jwt)):
192
+ report = execute_plan(goal)
193
+ return report
194
+
195
+ # ===========================================
196
+ # GRADIO FRONTEND
197
+ # ===========================================
198
+ def gradio_chat(message, history):
199
+ try:
200
+ response = agent.run(message)
201
+ history.append((message, response))
202
+ except Exception as e:
203
+ history.append((message, f"An error occurred: {str(e)}"))
204
+ return history, ""
205
+
206
+ def gradio_execute_plan(goal):
207
+ try:
208
+ return execute_plan(goal)
209
+ except Exception as e:
210
+ return {"error": f"Failed to execute plan: {str(e)}"}
211
+
212
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
213
+ gr.Markdown("# 🤖 Medini Autonomous Agent")
214
+ gr.Markdown("Chat or submit high-level goals. Agentic AI handles reasoning, memory, and tool use.")
215
+
216
+ with gr.Row():
217
+ with gr.Column(scale=2):
218
+ gr.Markdown("## Conversational Chat")
219
+ chatbot = gr.Chatbot(height=400)
220
+ msg = gr.Textbox(placeholder="Type your message...", label="Chat Input")
221
+ clear_btn = gr.Button("Clear Chat")
222
+ msg.submit(gradio_chat, [msg, chatbot], [chatbot, msg])
223
+ clear_btn.click(lambda: [], None, chatbot, queue=False)
224
+
225
+ with gr.Column(scale=1):
226
+ gr.Markdown("## Autonomous Goal Planner")
227
+ goal_input = gr.Textbox(placeholder="Enter high-level goal...", label="Goal")
228
+ run_goal_btn = gr.Button("Run Goal", variant="primary")
229
+ gr.Markdown("---")
230
+ gr.Markdown("### Execution Report")
231
+ goal_output = gr.JSON(label="Plan and Results")
232
+ run_goal_btn.click(gradio_execute_plan, [goal_input], goal_output)
233
+
234
+ # ===========================================
235
+ # LAUNCH
236
+ # ===========================================
237
+ if __name__ == "__main__":
238
+ def start_api():
239
+ uvicorn.run(app, host="0.0.0.0", port=8000, log_level="critical")
240
+ threading.Thread(target=start_api, daemon=True).start()
241
+ demo.launch(share=False)