Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,9 @@ __marimo__/

# Models
models/

# Logs
logs/

# DB
calendar.db
17 changes: 17 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"slot_duration_minutes": 30,
"min_gap_minutes": 15,
"max_appointments_per_day": 10,
"working_hours": {
"start": "09:00",
"end": "17:00"
},
"lunch_break": {
"start": "13:00",
"end": "14:00"
},
"non_working_days": [
"Saturday",
"Sunday"
]
}
124 changes: 124 additions & 0 deletions run_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# run_app.py
import gradio as gr
import json
from src.schedulebot.app import ChatbotApp

# --- Global variable to hold our chatbot instance ---
chatbot_instance = None


def save_config(
duration,
gap,
max_daily,
work_start,
work_end,
lunch_start,
lunch_end,
non_working_days,
):
"""Saves the user's configuration to config.json and initializes the chatbot."""
global chatbot_instance

config = {
"slot_duration_minutes": int(duration),
"min_gap_minutes": int(gap),
"max_appointments_per_day": int(max_daily),
"working_hours": {"start": work_start, "end": work_end},
"lunch_break": {"start": lunch_start, "end": lunch_end},
"non_working_days": non_working_days,
}

with open("config.json", "w") as f:
json.dump(config, f, indent=4)

print("--- Configuration Saved. Initializing Chatbot... ---")
# Initialize the main app class with the new config
chatbot_instance = ChatbotApp(
nlu_model_repo="andreaceto/schedulebot-nlu-engine", calendar_config=config
)
print("--- Chatbot Ready ---")

# Return updates to the Gradio UI
return {setup_box: gr.update(visible=False), chat_box: gr.update(visible=True)}


def chat_interface(message, history):
"""The function that Gradio will call for each user message."""
if chatbot_instance:
response = chatbot_instance.process_turn(message)
return response
return "Error: Chatbot not initialized. Please set the configuration first."


# --- Build the Gradio UI using Blocks ---
with gr.Blocks(theme=gr.themes.Default(), title="ScheduleBOT+") as demo:
gr.Markdown("# ScheduleBOT+ Configuration")

# --- State to manage visibility ---
is_configured = gr.State(False)

# --- Setup Screen (Visible by default) ---
with gr.Group(visible=True) as setup_box:
gr.Markdown("## Calendar Settings")
with gr.Row():
slot_duration = gr.Number(label="Slot Duration (minutes)", value=30)
min_gap = gr.Number(label="Min Gap Between (minutes)", value=15)
max_appointments = gr.Number(label="Max Appointments per Day", value=10)
gr.Markdown("### Working Hours")
with gr.Row():
working_start = gr.Textbox(label="Start (HH:MM)", value="09:00")
working_end = gr.Textbox(label="End (HH:MM)", value="17:00")
gr.Markdown("### Lunch Break")
with gr.Row():
lunch_start = gr.Textbox(label="Start (HH:MM)", value="13:00")
lunch_end = gr.Textbox(label="End (HH:MM)", value="14:00")

non_working = gr.CheckboxGroup(
label="Non-Working Days",
choices=[
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
],
value=["Saturday", "Sunday"],
)

save_button = gr.Button(
"Save Configuration and Start Chatbot", variant="primary"
)

# --- Chat Screen (Hidden by default) ---
with gr.Group(visible=False) as chat_box:
gr.ChatInterface(
fn=chat_interface,
title="ScheduleBOT+",
description="An intelligent agent for appointment management.",
examples=[
["Is 2 PM tomorrow available?"],
["I'd like to book a check-up with Dr. Smith."],
],
)

# --- UI Logic ---
save_button.click(
fn=save_config,
inputs=[
slot_duration,
min_gap,
max_appointments,
working_start,
working_end,
lunch_start,
lunch_end,
non_working,
],
outputs=[setup_box, chat_box],
)

if __name__ == "__main__":
demo.launch()
75 changes: 75 additions & 0 deletions src/schedulebot/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import logging
import json
from src.schedulebot.core.dialogue_manager import DialogueManager
from src.schedulebot.nlu.nlu_processor import NLUProcessor
from src.schedulebot.nlg.rule_based import NLGModule

# from src.schedulebot.nlg.slm_based import NLGModule
from src.schedulebot.core.tools import (
initialize_tools,
) # Import the new initializer function

# --- Setup Logging ---
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
filename="chatbot.log",
filemode="a",
)
logger = logging.getLogger(__name__)


class ChatbotApp:
"""
Orchestrates the entire NLU -> DM -> NLG pipeline for the chatbot.
"""

def __init__(self, nlu_model_repo: str, calendar_config: dict):
"""
Initializes all three core modules of the chatbot.
"""
self.nlu_processor = NLUProcessor(multitask_model_repo=nlu_model_repo)
self.dialogue_manager = DialogueManager()
self.nlg_module = NLGModule()

# --- NEW: Initialize tools with the calendar configuration ---
self.tool_registry = initialize_tools(calendar_config)

self.conversation_history = []
logger.info("ChatbotApp initialized successfully with custom configuration.")

def process_turn(self, user_input: str) -> str:
"""
Processes a single turn of the conversation from user input to bot response.
"""
nlu_output = self.nlu_processor.process(user_input)
logger.info(f"NLU Output: {json.dumps(nlu_output, indent=2)}")

action = self.dialogue_manager.get_next_action(nlu_output)
logger.info(f"DM Action: {json.dumps(action, indent=2)}")

# --- NEW: Execute the action using the tool registry ---
tool_name = action.get("action")

# The NLG module now generates the response based on the action
# For tool execution actions, we first call the tool, then generate a response
if tool_name in self.tool_registry:
try:
tool_function = self.tool_registry[tool_name]
tool_result = tool_function(**action.get("details", {}))
# Create a new action for the NLG module with the result
response_action = {
"action": f"respond_{tool_name}",
"details": {"result": tool_result},
}
bot_response = self.nlg_module.generate_response(response_action)
except TypeError as e:
logger.error(f"Error calling tool '{tool_name}': {e}")
bot_response = self.nlg_module.generate_response({"action": "fallback"})
else:
# If it's not a tool, it's a direct NLG action (like greet, confirm, etc.)
bot_response = self.nlg_module.generate_response(action)

logger.info(f"NLG Response: {bot_response}")

return bot_response
Loading
Loading