kendrickfff commited on
Commit
551e92e
·
verified ·
1 Parent(s): 4a6d4a0

Upload app (2).py

Browse files
Files changed (1) hide show
  1. app (2).py +180 -0
app (2).py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Agent Ken — PM + Social Impact Copilot
3
+ Hugging Face Space (Gradio) → Azure AI Foundry Agent Service
4
+ """
5
+
6
+ import os
7
+ import gradio as gr
8
+ from azure.ai.projects import AIProjectClient
9
+ from azure.identity import ClientSecretCredential
10
+ from azure.ai.agents.models import ListSortOrder
11
+
12
+ # ── Azure config (from HF Space secrets) ──────────────────────────
13
+ PROJECT_ENDPOINT = os.environ["PROJECT_ENDPOINT"]
14
+ AGENT_ID = os.environ["AGENT_ID"]
15
+ AZURE_TENANT_ID = os.environ["AZURE_TENANT_ID"]
16
+ AZURE_CLIENT_ID = os.environ["AZURE_CLIENT_ID"]
17
+ AZURE_CLIENT_SECRET = os.environ["AZURE_CLIENT_SECRET"]
18
+
19
+ # ── Azure client (singleton) ──────────────────────────────────────
20
+ credential = ClientSecretCredential(
21
+ tenant_id=AZURE_TENANT_ID,
22
+ client_id=AZURE_CLIENT_ID,
23
+ client_secret=AZURE_CLIENT_SECRET,
24
+ )
25
+ project = AIProjectClient(credential=credential, endpoint=PROJECT_ENDPOINT)
26
+ agent = project.agents.get_agent(AGENT_ID)
27
+
28
+
29
+ # ── Chat logic ────────────────────────────────────────────────────
30
+ def respond(user_message: str, history: list, thread_state: dict | None):
31
+ """
32
+ Gradio calls this for every user message.
33
+ - Creates a new Azure thread on first message (per session).
34
+ - Sends user message → runs agent → returns assistant reply.
35
+ """
36
+ if not user_message.strip():
37
+ return history, thread_state
38
+
39
+ # First message in session → create new thread
40
+ if thread_state is None or "thread_id" not in thread_state:
41
+ thread = project.agents.threads.create()
42
+ thread_state = {"thread_id": thread.id}
43
+
44
+ thread_id = thread_state["thread_id"]
45
+
46
+ try:
47
+ # Send user message
48
+ project.agents.messages.create(
49
+ thread_id=thread_id,
50
+ role="user",
51
+ content=user_message,
52
+ )
53
+
54
+ # Run agent
55
+ run = project.agents.runs.create_and_process(
56
+ thread_id=thread_id,
57
+ agent_id=agent.id,
58
+ )
59
+
60
+ if run.status == "failed":
61
+ assistant_reply = (
62
+ f"⚠️ Agent run failed: {run.last_error}\n\n"
63
+ "Please try again or start a new conversation."
64
+ )
65
+ else:
66
+ # Get latest assistant message
67
+ messages = project.agents.messages.list(
68
+ thread_id=thread_id, order=ListSortOrder.DESCENDING
69
+ )
70
+ assistant_reply = "🤔 No response received."
71
+ for msg in messages:
72
+ if msg.role == "assistant" and msg.text_messages:
73
+ assistant_reply = msg.text_messages[-1].text.value
74
+ break
75
+
76
+ except Exception as e:
77
+ assistant_reply = (
78
+ f"❌ Error: {str(e)}\n\n"
79
+ "Please try again. If this persists, the Azure endpoint may be unavailable."
80
+ )
81
+
82
+ history.append({"role": "user", "content": user_message})
83
+ history.append({"role": "assistant", "content": assistant_reply})
84
+ return history, thread_state
85
+
86
+
87
+ def new_conversation():
88
+ """Reset chat history and thread state."""
89
+ return [], None
90
+
91
+
92
+ # ── Gradio UI ─────────────────────────────────────────────────────
93
+ DESCRIPTION = """
94
+ # 🤖 Agent Ken — PM + Social Impact Copilot
95
+
96
+ A **Product Manager Copilot** with a social worker's empathy and harm-reduction lens.
97
+ Built on Azure AI Foundry Agent Service.
98
+
99
+ **What I can help with:**
100
+ - 🔍 Product discovery (JTBD, personas, problem statements)
101
+ - 📊 Prioritization (RICE scoring, MoSCoW)
102
+ - 📝 PRDs, user stories, acceptance criteria
103
+ - 🧪 Experiment design (A/B tests, hypothesis templates)
104
+ - 📈 Metrics & OKRs (North Star, funnels, guardrails)
105
+ - 🤝 Stakeholder communication (memos, updates, FAQs)
106
+ - ♿ Inclusive & ethical design considerations
107
+
108
+ ---
109
+ *Created by Kendrick Filbert • Powered by Azure AI Foundry*
110
+ """
111
+
112
+ EXAMPLES = [
113
+ "Help me write a PRD for a mobile mental health check-in feature",
114
+ "Score these 3 features using RICE: onboarding revamp, dark mode, referral program",
115
+ "Design an A/B test for our new checkout flow",
116
+ "What metrics should I track for a community-driven marketplace?",
117
+ "Help me draft a stakeholder memo about delaying our launch by 2 weeks",
118
+ "Review this user story for accessibility concerns",
119
+ ]
120
+
121
+ CSS = """
122
+ .gradio-container { max-width: 850px !important; }
123
+ footer { display: none !important; }
124
+ """
125
+
126
+ with gr.Blocks(css=CSS, title="Agent Ken — PM Copilot", theme=gr.themes.Soft()) as demo:
127
+
128
+ gr.Markdown(DESCRIPTION)
129
+
130
+ chatbot = gr.Chatbot(
131
+ label="Agent Ken",
132
+ height=500,
133
+ type="messages",
134
+ avatar_images=(None, "https://em-content.zobj.net/source/twitter/408/robot_1f916.png"),
135
+ show_copy_button=True,
136
+ )
137
+
138
+ # State: thread_id per session (not visible to user)
139
+ thread_state = gr.State(value=None)
140
+
141
+ with gr.Row():
142
+ msg_input = gr.Textbox(
143
+ placeholder="Ask Agent Ken anything about product management...",
144
+ label="Your message",
145
+ scale=5,
146
+ lines=1,
147
+ max_lines=4,
148
+ )
149
+ send_btn = gr.Button("Send 🚀", variant="primary", scale=1)
150
+
151
+ with gr.Row():
152
+ clear_btn = gr.Button("🗑️ New Conversation", variant="secondary")
153
+
154
+ with gr.Accordion("💡 Example prompts", open=False):
155
+ gr.Examples(
156
+ examples=EXAMPLES,
157
+ inputs=msg_input,
158
+ )
159
+
160
+ # ── Event bindings ────────────────────────────────────────────
161
+ send_btn.click(
162
+ fn=respond,
163
+ inputs=[msg_input, chatbot, thread_state],
164
+ outputs=[chatbot, thread_state],
165
+ ).then(lambda: "", outputs=msg_input)
166
+
167
+ msg_input.submit(
168
+ fn=respond,
169
+ inputs=[msg_input, chatbot, thread_state],
170
+ outputs=[chatbot, thread_state],
171
+ ).then(lambda: "", outputs=msg_input)
172
+
173
+ clear_btn.click(
174
+ fn=new_conversation,
175
+ outputs=[chatbot, thread_state],
176
+ )
177
+
178
+ # ── Launch ────────────────────────────────────────────────────────
179
+ if __name__ == "__main__":
180
+ demo.launch(server_name="0.0.0.0", server_port=7860)