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
33 changes: 19 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,45 @@ SHELL := /bin/bash
# Default command is 'help'
.DEFAULT_GOAL := help

## --------------------------------------
## Application Commands
## --------------------------------------

.PHONY: run
run: ## Run the Gradio application
@echo ">> Starting the ScheduleBOT+ application..."
@python run_app.py

## --------------------------------------
## Docker Commands
## --------------------------------------

.PHONY: build
build: ## 🛠️ Build or rebuild the Docker services
build: ## Build or rebuild the Docker services
@echo ">> Building services..."
@docker-compose build

.PHONY: up
up: ## 🚀 Start all services in detached mode
up: ## Start backend services (like Duckling) in detached mode
@echo ">> Starting services in the background..."
@docker-compose up -d

.PHONY: down
down: ## 🛑 Stop and remove all services
down: ## Stop and remove all Docker services
@echo ">> Stopping and removing containers..."
@docker-compose down

.PHONY: logs
logs: ## 📜 View real-time logs for all services
@echo ">> Tailing logs (press Ctrl+C to stop)..."
@docker-compose logs -f

.PHONY: test
test: ## 🧪 Run pytest inside the app container
@echo ">> Running tests..."
@docker-compose run --rm app pytest

## --------------------------------------
## Help
## --------------------------------------

.PHONY: help
help: ## 🙋 Show this help message
help: ## Show this help message
@echo "Available commands:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_-LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
@echo ""
@echo " build Build or rebuild the Docker services"
@echo " up Start backend services (like Duckling) in detached mode"
@echo " run Run the Gradio application"
@echo " down Stop and remove all Docker services"
@echo " help Show this help message"
2 changes: 1 addition & 1 deletion docker/app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ COPY .env /app/.env
# Command to run the application when the container launches
# This will be the main entry point for your Gradio app in the next milestone.
# For now, we can use the command-line chat.
CMD ["python", "-m", "src.schedulebot.main"]
CMD ["python", "-m", "run_app.py"]
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ seqeval
accelerate
bitsandbytes
gradio
pytest
27 changes: 16 additions & 11 deletions run_app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# run_app.py
import gradio as gr
import json
from src.schedulebot.app import ChatbotApp
Expand Down Expand Up @@ -39,8 +38,13 @@ def save_config(
)
print("--- Chatbot Ready ---")

# Return updates to the Gradio UI
return {setup_box: gr.update(visible=False), chat_box: gr.update(visible=True)}
# Return updates to the Gradio UI to hide the setup and show the chat
return {
# --- NEW: Hide the main title ---
main_title: gr.update(visible=False),
setup_group: gr.update(visible=False),
chat_group: gr.update(visible=True),
}


def chat_interface(message, history):
Expand All @@ -53,13 +57,11 @@ def chat_interface(message, history):

# --- 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)
# --- NEW: Create a separate component for the title ---
main_title = gr.Markdown("# ScheduleBOT+ Configuration")

# --- Setup Screen (Visible by default) ---
with gr.Group(visible=True) as setup_box:
with gr.Group(visible=True) as setup_group:
gr.Markdown("## Calendar Settings")
with gr.Row():
slot_duration = gr.Number(label="Slot Duration (minutes)", value=30)
Expand Down Expand Up @@ -93,13 +95,14 @@ def chat_interface(message, history):
)

# --- Chat Screen (Hidden by default) ---
with gr.Group(visible=False) as chat_box:
with gr.Group(visible=False) as chat_group:
gr.ChatInterface(
fn=chat_interface,
title="ScheduleBOT+",
description="An intelligent agent for appointment management.",
examples=[
["Is 2 PM tomorrow available?"],
["Hi there!"],
["Is 2 PM tomorrow available for a dental cleaning?"],
["I'd like to book a check-up with Dr. Smith."],
],
)
Expand All @@ -117,8 +120,10 @@ def chat_interface(message, history):
lunch_end,
non_working,
],
outputs=[setup_box, chat_box],
# --- NEW: Add the title to the outputs ---
outputs=[main_title, setup_group, chat_group],
)


if __name__ == "__main__":
demo.launch()
Empty file added src/__init__.py
Empty file.
41 changes: 28 additions & 13 deletions src/schedulebot/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
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
Expand Down Expand Up @@ -32,7 +30,6 @@ def __init__(self, nlu_model_repo: str, calendar_config: dict):
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 = []
Expand All @@ -48,28 +45,46 @@ def process_turn(self, user_input: str) -> str:
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},
}

if tool_result.get("success"):
# For successful tool calls, use a specific response action
response_action = {
"action": f"respond_{tool_name}",
"details": {"result": tool_result.get("message")},
}
else:
# Handle failures and suggestions
if tool_result.get("suggestions"):
suggestions_str = ", ".join(
[s.strftime("%I:%M %p") for s in tool_result["suggestions"]]
)
response_action = {
"action": "suggest_slots",
"details": {
"reason": tool_result.get("message"),
"suggestions": suggestions_str,
},
}
else:
response_action = {
"action": "inform_failure",
"details": tool_result,
}

bot_response = self.nlg_module.generate_response(response_action)
except TypeError as e:

except Exception 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