Spaces:
Runtime error
Runtime error
| from dotenv import load_dotenv | |
| from openai import OpenAI | |
| import json | |
| import os | |
| import requests | |
| import gradio as gr | |
| from pathlib import Path | |
| load_dotenv(override=True) | |
| def push(text): | |
| requests.post( | |
| "https://api.pushover.net/1/messages.json", | |
| data={ | |
| "token": os.getenv("PUSHOVER_TOKEN"), | |
| "user": os.getenv("PUSHOVER_USER"), | |
| "message": text, | |
| } | |
| ) | |
| def record_user_details(email, name="Name not provided", notes="not provided"): | |
| push(f"Recording {name} with email {email} and notes {notes}") | |
| return {"recorded": "ok"} | |
| def record_unknown_question(question): | |
| push(f"Recording {question}") | |
| return {"recorded": "ok"} | |
| record_user_details_json = { | |
| "name": "record_user_details", | |
| "description": "Use this tool to record that a user is interested in being in touch and provided an email address", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "email": { | |
| "type": "string", | |
| "description": "The email address of this user" | |
| }, | |
| "name": { | |
| "type": "string", | |
| "description": "The user's name, if they provided it" | |
| } | |
| , | |
| "notes": { | |
| "type": "string", | |
| "description": "Any additional information about the conversation that's worth recording to give context" | |
| } | |
| }, | |
| "required": ["email"], | |
| "additionalProperties": False | |
| } | |
| } | |
| record_unknown_question_json = { | |
| "name": "record_unknown_question", | |
| "description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "question": { | |
| "type": "string", | |
| "description": "The question that couldn't be answered" | |
| }, | |
| }, | |
| "required": ["question"], | |
| "additionalProperties": False | |
| } | |
| } | |
| tools = [{"type": "function", "function": record_user_details_json}, | |
| {"type": "function", "function": record_unknown_question_json}] | |
| class Me: | |
| def __init__(self): | |
| self.openai = OpenAI() | |
| districts = [] | |
| for p in Path("districts").glob("*.json"): | |
| with p.open("r", encoding="utf-8") as f: | |
| districts.append(json.load(f)) | |
| self.districts = districts | |
| def handle_tool_call(self, tool_calls): | |
| results = [] | |
| for tool_call in tool_calls: | |
| tool_name = tool_call.function.name | |
| arguments = json.loads(tool_call.function.arguments) | |
| print(f"Tool called: {tool_name}", flush=True) | |
| tool = globals().get(tool_name) | |
| result = tool(**arguments) if tool else {} | |
| results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id}) | |
| return results | |
| def system_prompt(self): | |
| system_prompt = f""" | |
| You are the Worship Service Planner for the user. | |
| Scope: | |
| - You focus exclusively on the Iglesia Ni Cristo (Church of Christ) worship services. | |
| - Politely decline or redirect any questions not related to INC worship services. | |
| Knowledge Source: | |
| - Answer only using the data provided here: {self.districts}. | |
| - Do not use outside knowledge. If the data is insufficient, say so and follow the fallback guidance below. | |
| Primary Objectives: | |
| - Answer user questions about worship services (e.g., locations, schedules, planning suitable days/times). | |
| - When answering questions, try to find the nearest applicable location from the data and share its address. | |
| - When you are asked for a planning suitable days/times, try to find the nearest planning suitable days/times to the user's location. | |
| - Encourage contacting the church office or local officers for confirmation and details when information is incomplete or uncertain. Share the contact information for the church office or local officers (e.g., phone number). | |
| - Always respond with a disclaimer to check and verify the information with the church office or local officers and share the contact information for the church office or local officers. | |
| Behavior and Style: | |
| - Be professional, respectful, and concise. | |
| - Ask brief clarifying questions if needed (e.g., city/ZIP, preferred day/time) to provide accurate guidance. | |
| - When offering options, list the most relevant or nearest 1–3 locales with names and addresses. | |
| Fallbacks and Unknowns: | |
| - If you can’t find an exact match, choose the nearest applicable location from the data and share its address. | |
| - Encourage contacting the church office or local officers for confirmation and details when information is incomplete or uncertain. | |
| - If you cannot answer a question from the provided data, clearly state that and proceed with the nearest-location guidance above. | |
| Tool Usage: | |
| - If the user appears interested in follow-up or shares an email, ask for their email (and name if offered) and record it using the record_user_details tool. | |
| - If a question cannot be answered due to missing data, record it using the record_unknown_question tool. | |
| Stay in Character: | |
| - Always respond as the Worship Service Planner for the Iglesia Ni Cristo. | |
| """ | |
| system_prompt += f"With this context, please chat with the user, always staying in character as Worship Service Planner for the Iglesia Ni Cristo." | |
| return system_prompt | |
| def chat(self, message, history): | |
| messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}] | |
| done = False | |
| while not done: | |
| response = self.openai.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools) | |
| if response.choices[0].finish_reason=="tool_calls": | |
| message = response.choices[0].message | |
| tool_calls = message.tool_calls | |
| results = self.handle_tool_call(tool_calls) | |
| messages.append(message) | |
| messages.extend(results) | |
| else: | |
| done = True | |
| return response.choices[0].message.content | |
| if __name__ == "__main__": | |
| me = Me() | |
| gr.ChatInterface(me.chat, type="messages").launch() | |