eaglelandsonce commited on
Commit
7a3cfaa
·
verified ·
1 Parent(s): 4587501

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +218 -0
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from typing import List, Dict, Optional, Set, Callable, Any
3
+
4
+ # Azure AI Agents SDK (API key auth)
5
+ from azure.core.credentials import AzureKeyCredential
6
+ from azure.ai.agents import AgentsClient
7
+ from azure.ai.agents.models import (
8
+ FunctionTool,
9
+ ToolSet,
10
+ ListSortOrder,
11
+ MessageRole,
12
+ )
13
+
14
+ from user_functions import user_functions # your callable tool(s)
15
+
16
+
17
+ # ---------- Core Agent Helpers ----------
18
+
19
+ def init_agent(
20
+ endpoint: str,
21
+ api_key: str,
22
+ model_deployment: str,
23
+ ) -> dict:
24
+ """
25
+ Initialize an Azure AI Agent with a custom FunctionTool.
26
+ Returns a session dict containing client, agent_id, thread_id, etc.
27
+ """
28
+ if not endpoint or not api_key or not model_deployment:
29
+ raise ValueError("Please provide endpoint, key, and model deployment name.")
30
+
31
+ client = AgentsClient(
32
+ endpoint=endpoint.strip(),
33
+ credential=AzureKeyCredential(api_key.strip()),
34
+ )
35
+
36
+ # Build toolset with your functions and enable auto function calls
37
+ functions_tool = FunctionTool(user_functions)
38
+ toolset = ToolSet()
39
+ toolset.add(functions_tool)
40
+
41
+ client.enable_auto_function_calls(toolset)
42
+
43
+ # Create the agent
44
+ agent = client.create_agent(
45
+ model=model_deployment.strip(),
46
+ name="support-agent",
47
+ instructions=(
48
+ "You are a technical support agent. "
49
+ "When a user has a technical issue, ask for their email address and a description "
50
+ "of the issue. Then submit a support ticket using the available function. "
51
+ "If a file is saved, tell the user the file name."
52
+ ),
53
+ toolset=toolset,
54
+ )
55
+
56
+ # Create a thread for the conversation
57
+ thread = client.threads.create()
58
+
59
+ # Session we keep in Gradio state
60
+ return {
61
+ "endpoint": endpoint.strip(),
62
+ "api_key": api_key.strip(),
63
+ "model": model_deployment.strip(),
64
+ "client": client,
65
+ "agent_id": agent.id,
66
+ "thread_id": thread.id,
67
+ }
68
+
69
+
70
+ def send_to_agent(user_msg: str, session: dict):
71
+ """
72
+ Send message to the agent thread and return:
73
+ - agent_reply (str)
74
+ - history_str (str) chronological log
75
+ """
76
+ if not session or "client" not in session:
77
+ raise ValueError("Agent is not initialized. Click 'Connect & Prepare' first.")
78
+
79
+ client: AgentsClient = session["client"]
80
+ agent_id = session["agent_id"]
81
+ thread_id = session["thread_id"]
82
+
83
+ # Add user message
84
+ client.messages.create(
85
+ thread_id=thread_id,
86
+ role="user",
87
+ content=user_msg,
88
+ )
89
+
90
+ # Run and wait (auto function-calls enabled)
91
+ run = client.runs.create_and_process(thread_id=thread_id, agent_id=agent_id)
92
+ if getattr(run, "status", None) == "failed":
93
+ last_error = getattr(run, "last_error", "Unknown error")
94
+ return f"Run failed: {last_error}", ""
95
+
96
+ # Last agent message
97
+ last_msg = client.messages.get_last_message_text_by_role(
98
+ thread_id=thread_id,
99
+ role=MessageRole.AGENT,
100
+ )
101
+ agent_reply = last_msg.text.value if last_msg else "(No reply text found.)"
102
+
103
+ # Build readable history (chronological)
104
+ history_lines = []
105
+ messages = client.messages.list(thread_id=thread_id, order=ListSortOrder.ASCENDING)
106
+ for m in messages:
107
+ if m.text_messages:
108
+ last_text = m.text_messages[-1].text.value
109
+ history_lines.append(f"{m.role}: {last_text}")
110
+ history_str = "\n\n".join(history_lines)
111
+
112
+ return agent_reply, history_str
113
+
114
+
115
+ def teardown(session: dict) -> str:
116
+ """
117
+ Delete the agent to reduce costs. (Threads are retained by the service.)
118
+ """
119
+ if not session:
120
+ return "Nothing to clean up."
121
+
122
+ notes = []
123
+ try:
124
+ client: AgentsClient = session.get("client")
125
+ agent_id = session.get("agent_id")
126
+ if client and agent_id:
127
+ client.delete_agent(agent_id)
128
+ notes.append("Deleted agent.")
129
+ except Exception as e:
130
+ notes.append(f"Cleanup warning: {e}")
131
+
132
+ return " ".join(notes) if notes else "Cleanup complete."
133
+
134
+
135
+ # ---------- Gradio UI ----------
136
+
137
+ with gr.Blocks(title="Azure AI Support Agent (Functions) — Gradio") as demo:
138
+ gr.Markdown(
139
+ "## Azure AI Support Agent (Custom Function Tool)\n"
140
+ "Enter your **Project Endpoint** and **Key**, set your **Model Deployment** (e.g., `gpt-4o`), then chat.\n"
141
+ "The agent can call a custom function to **submit a support ticket** and save it as a file."
142
+ )
143
+
144
+ with gr.Row():
145
+ endpoint = gr.Textbox(label="Project Endpoint", placeholder="https://<your-project-endpoint>")
146
+ api_key = gr.Textbox(label="Project Key", placeholder="paste your key", type="password")
147
+
148
+ with gr.Row():
149
+ model = gr.Textbox(label="Model Deployment Name", value="gpt-4o")
150
+
151
+ session_state = gr.State(value=None)
152
+
153
+ connect_btn = gr.Button("🔌 Connect & Prepare Agent", variant="primary")
154
+ connect_status = gr.Markdown("")
155
+
156
+ with gr.Row():
157
+ chatbot = gr.Chatbot(
158
+ label="Conversation",
159
+ height=420,
160
+ type="messages", # openai-style dicts: {"role": "...", "content": "..."}
161
+ )
162
+
163
+ user_input = gr.Textbox(label="Your message", placeholder="e.g., I have a technical problem")
164
+ with gr.Row():
165
+ send_btn = gr.Button("Send ▶")
166
+ cleanup_btn = gr.Button("Delete Agent & Cleanup 🧹")
167
+
168
+ history = gr.Textbox(label="Conversation Log (chronological)", lines=12)
169
+
170
+ # ----- Callbacks -----
171
+
172
+ def on_connect(ep, key, mdl):
173
+ try:
174
+ sess = init_agent(ep, key, mdl)
175
+ return sess, "✅ Connected. Support agent and thread are ready."
176
+ except Exception as e:
177
+ return None, f"❌ Connection error: {e}"
178
+
179
+ connect_btn.click(
180
+ fn=on_connect,
181
+ inputs=[endpoint, api_key, model],
182
+ outputs=[session_state, connect_status],
183
+ )
184
+
185
+ def on_send(msg: str, session: dict, chat_msgs: List[Dict[str, str]]):
186
+ if not msg:
187
+ return gr.update(), gr.update(), gr.update(value="Please enter a message.")
188
+ try:
189
+ agent_reply, log = send_to_agent(msg, session)
190
+ chat_msgs = (chat_msgs or []) + [
191
+ {"role": "user", "content": msg},
192
+ {"role": "assistant", "content": agent_reply},
193
+ ]
194
+ return chat_msgs, "", gr.update(value=log) # clear input, update log
195
+ except Exception as e:
196
+ return chat_msgs, msg, gr.update(value=f"❌ Error: {e}")
197
+
198
+ send_btn.click(
199
+ fn=on_send,
200
+ inputs=[user_input, session_state, chatbot],
201
+ outputs=[chatbot, user_input, history],
202
+ )
203
+
204
+ def on_cleanup(session):
205
+ try:
206
+ msg = teardown(session)
207
+ return None, f"🧹 {msg}"
208
+ except Exception as e:
209
+ return session, f"⚠️ Cleanup error: {e}"
210
+
211
+ cleanup_btn.click(
212
+ fn=on_cleanup,
213
+ inputs=[session_state],
214
+ outputs=[session_state, connect_status],
215
+ )
216
+
217
+ if __name__ == "__main__":
218
+ demo.launch()