File size: 6,277 Bytes
63f24b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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()