setup assisant bot

This commit is contained in:
OwusuBlessing
2025-07-24 14:27:56 +01:00
commit 95d357de60
62 changed files with 1106 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
OPENAI_API_KEY="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"
API_KEY="drbot-nhLybL86VBiUTtyngshsdj9efmc"
+2
View File
@@ -0,0 +1,2 @@
OPENAI_API_KEY="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"
API_KEY="drbot-nhLybL86VBiUTtyngshsdj9efmc"
View File
View File
View File
View File
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
Binary file not shown.
+19
View File
@@ -0,0 +1,19 @@
from pydantic_settings import BaseSettings
from functools import lru_cache
from config import Config
class Settings(BaseSettings):
API_V1_STR: str = "/api/v1"
PROJECT_NAME: str = "drone bot API"
VERSION: str = "1.0.0"
DESCRIPTION: str = ""
# API Key validation
API_KEY_ACCESS: str = Config.API_KEY
class Config:
case_sensitive = True
@lru_cache()
def get_settings() -> Settings:
return Settings()
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
from fastapi import HTTPException, Depends, Security
from fastapi.security import APIKeyHeader
from api.models.requests import BaseRequest
from config import Config
api_key_header = APIKeyHeader(name="Authorization", auto_error=False)
async def get_api_key(api_key_header: str = Security(api_key_header)) -> str:
"""Validate API key from header"""
if not api_key_header or not api_key_header.startswith('Bearer '):
raise HTTPException(
status_code=401,
detail={"error": "Unauthorized", "message": "API key is missing or invalid."}
)
token = api_key_header.split(' ')[1]
if token != Config.API_KEY:
raise HTTPException(
status_code=401,
detail={"error": "Unauthorized", "message": "API key does not match."}
)
return token
Binary file not shown.
Binary file not shown.
+15
View File
@@ -0,0 +1,15 @@
# api/models/requests.py
from pydantic import BaseModel
from typing import List
class BaseRequest(BaseModel):
pass
class ChatMessage(BaseModel):
role: str # "human" or "ai"
content: str
class ChatRequest(BaseModel):
query: str
history: List[ChatMessage] = []
+8
View File
@@ -0,0 +1,8 @@
# api/models/responses.py
from pydantic import BaseModel
class ChatResponse(BaseModel):
status: str
message: str
View File
Binary file not shown.
Binary file not shown.
+38
View File
@@ -0,0 +1,38 @@
# api/routes/chat_ai.py
from fastapi import APIRouter, Depends, HTTPException
from api.models.requests import ChatRequest, ChatMessage
from api.models.responses import ChatResponse
from api.dependencies.auth import get_api_key
from src.llm.orchestrator import DroneBot, Message # Adjust import as needed
router = APIRouter(
prefix="/chat-ai",
tags=["chat"]
)
@router.post("", response_model=ChatResponse)
async def chat_ai(
request: ChatRequest,
_: str = Depends(get_api_key)
):
"""Chat with DroneBot using query and history."""
try:
# Convert to internal Message format
history = [Message(role=msg.role, content=msg.content) for msg in request.history]
# Initialize DroneBot with history
bot = DroneBot(history=history, use_openai_as_fallback=True)
# Get response
result = bot.chat(request.query)
return ChatResponse(
status="success",
message=result["final_message"]
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=str(e)
)
+15
View File
@@ -0,0 +1,15 @@
from dotenv import load_dotenv
import os
import json
import asyncio
import random
load_dotenv(override=True)
class Config:
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
API_KEY = os.getenv("API_KEY")
+40
View File
@@ -0,0 +1,40 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from api.routes import chat
from api.config import Settings
settings = Settings()
app = FastAPI(
title=settings.PROJECT_NAME,
description=settings.DESCRIPTION,
version=settings.VERSION,
openapi_url=f"{settings.API_V1_STR}/openapi.json",
docs_url=f"{settings.API_V1_STR}/docs",
redoc_url=f"{settings.API_V1_STR}/redoc",
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Modify this in production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include routers
app.include_router(chat.router, prefix=settings.API_V1_STR)
@app.get("/")
async def root():
return {
"message": "Welcome to DroneBot AI API",
"version": settings.VERSION,
"docs_url": f"{settings.API_V1_STR}/docs"
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5120)
View File
+21
View File
@@ -0,0 +1,21 @@
numpy==1.26.4
openai==1.58.1
pandas==2.2.3
python-dotenv==1.0.1
reportlab==4.2.5
requests==2.32.3
fastapi
Jinja2==3.1.5
matplotlib==3.8.2
slack_sdk==3.34.0
tabulate==0.9.0
python-multipart==0.0.20
langgraph==0.3.2
langchain==0.3.19
langchain-openai== 0.3.7
langchain-anthropic==0.3.9
gunicorn==23.0.0
tqdm
uvicorn[standard]
cryptography
pydantic-settings
View File
View File
Binary file not shown.
View File
View File
View File
Binary file not shown.
Binary file not shown.
+60
View File
@@ -0,0 +1,60 @@
class LlmConfig:
class openai:
class models:
gpt_4o = "gpt-4o"
gpt_4_1 = "gpt-4.1"
temperatures = {
"default": 0.7,
"drone_bot": 0.3,
"creative": 0.9
}
max_tokens = {
"default": 2048,
"summary_bot": 512,
"explainer": 1024
}
class anthropic:
class models:
claude_3_opus = "claude-3-opus"
claude_3_sonnet = "claude-3-sonnet"
temperatures = {
"default": 0.5,
"drone_bot": 0.4,
"research": 0.2
}
max_tokens = {
"default": 4096,
"summary_bot": 1024
}
@classmethod
def get_config(cls, provider: str, model_name: str, temp_name: str = "default", token_preset: str = "default") -> dict:
if not hasattr(cls, provider):
raise ValueError(f"Provider '{provider}' not found.")
provider_cls = getattr(cls, provider)
# Get model value from class (e.g., LlmConfig.openai.models.gpt_4o)
model_cls = getattr(provider_cls, "models")
if not hasattr(model_cls, model_name):
raise ValueError(f"Model '{model_name}' not found under provider '{provider}'.")
model = getattr(model_cls, model_name)
if temp_name not in provider_cls.temperatures:
raise ValueError(f"Temperature preset '{temp_name}' not found under provider '{provider}'.")
if token_preset not in provider_cls.max_tokens:
raise ValueError(f"Max token preset '{token_preset}' not found under provider '{provider}'.")
return {
"provider": provider,
"model": model,
"temperature": provider_cls.temperatures[temp_name],
"max_tokens": provider_cls.max_tokens[token_preset]
}
View File
Binary file not shown.
Binary file not shown.
View File
View File
+446
View File
@@ -0,0 +1,446 @@
import json
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
import os
from typing import List, Dict, Any, Optional
from typing_extensions import TypedDict
from langchain_core.prompts import ChatPromptTemplate
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage, SystemMessage
from dataclasses import dataclass
import random
import time
from functools import wraps
from src.prompts.setup_prompt import setup_prompt_manager
from src.config.llm_config import LlmConfig
from src.llm.tools import AgenTools
from config import Config
prompt_manager = setup_prompt_manager()
@dataclass
class Message:
role: str # 'human' or 'ai'
content: str
class ModelRotationManager:
"""Manages model rotation and failure tracking"""
def __init__(self, use_openai_as_fallback: bool = True):
# Base Anthropic models
self.models = [
# {
# "name": "claude-3-5-sonnet-latest",
# "provider": "anthropic",
# "api_key": os.getenv("ANTHROPIC_API_KEY"),
# "temperature": 0.001
# }
]
# Add OpenAI models if enabled
if use_openai_as_fallback:
openai_models = [
{
"name": LlmConfig.openai.models.gpt_4o,
"provider": "openai",
"api_key": Config.OPENAI_API_KEY,
"temperature": LlmConfig.openai.temperatures.get("drone_bot")
},
{
"name": LlmConfig.openai.models.gpt_4_1,
"provider": "openai",
"api_key": Config.OPENAI_API_KEY,
"temperature": LlmConfig.openai.temperatures.get("drone_bot")
}
]
self.models.extend(openai_models)
# Track failures per model
self.failure_counts = {model["name"]: 0 for model in self.models}
self.current_model_index = 0
self.max_failures_per_model = 2
self.use_openai_as_fallback = use_openai_as_fallback
print(f"ModelRotationManager initialized with {len(self.models)} models")
print(f"OpenAI fallback: {'Enabled' if use_openai_as_fallback else 'Disabled'}")
for model in self.models:
print(f" - {model['name']} ({model['provider']})")
def get_current_model(self):
"""Get the current model configuration"""
return self.models[self.current_model_index]
def create_llm_instance(self, model_config: dict):
"""Create an LLM instance based on model configuration"""
if model_config["provider"] == "anthropic":
return ChatAnthropic(
api_key=model_config["api_key"],
model=model_config["name"],
temperature=model_config["temperature"]
)
elif model_config["provider"] == "openai":
return ChatOpenAI(
api_key=model_config["api_key"],
model=model_config["name"],
temperature=model_config["temperature"]
)
else:
raise ValueError(f"Unsupported provider: {model_config['provider']}")
def rotate_on_failure(self, failed_model_name: str):
"""Rotate to next model on failure"""
self.failure_counts[failed_model_name] += 1
print(f"Model {failed_model_name} failed. Failure count: {self.failure_counts[failed_model_name]}")
# Find next available model
attempts = 0
while attempts < len(self.models):
self.current_model_index = (self.current_model_index + 1) % len(self.models)
next_model = self.models[self.current_model_index]
if self.failure_counts[next_model["name"]] < self.max_failures_per_model:
print(f"Rotating to model: {next_model['name']}")
return next_model
attempts += 1
# If all models have failed, reset and try again
print("All models have failed. Resetting failure counts and trying again.")
self.failure_counts = {model["name"]: 0 for model in self.models}
self.current_model_index = 0
return self.models[0]
def get_next_model(self):
"""Get next available model"""
current_model = self.get_current_model()
if self.failure_counts[current_model["name"]] < self.max_failures_per_model:
return current_model
else:
return self.rotate_on_failure(current_model["name"])
class State(TypedDict):
messages: List[HumanMessage | AIMessage]
final_response: Optional[str]
user_question: Optional[str]
current_model: Optional[str]
class DroneBot:
def __init__(self, history: Optional[List[Message]] = None, use_openai_as_fallback: bool = True):
"""
Initialize DroneBot - a simplified plotting agent
Args:
history: Optional conversation history
use_openai_as_fallback: Whether to include OpenAI models as fallback
"""
self.history = history if history is not None else []
self.use_openai_as_fallback = use_openai_as_fallback
# Initialize model rotation manager
self.model_manager = ModelRotationManager(use_openai_as_fallback=use_openai_as_fallback)
# Initialize output variables
self.final_message = ""
self.final_model_used = ""
# Create tools
self.tools = self._create_tools()
print(f"DroneBot initialized")
print(f"OpenAI fallback: {'Enabled' if use_openai_as_fallback else 'Disabled'}")
def _create_tools(self):
"""define tools for agent"""
# Mock tool definition - replace with your actual tool
tools = AgenTools.tools
return tools
def _extract_final_message_content(self, response: AIMessage) -> str:
"""Extract the final message content from an AIMessage response"""
try:
# Case 1: Response has direct string content
if hasattr(response, 'content') and isinstance(response.content, str) and response.content.strip():
return response.content.strip()
# Case 2: Response has list content (complex format)
elif hasattr(response, 'content') and isinstance(response.content, list):
for item in response.content:
if isinstance(item, dict) and item.get('type') == 'text' and item.get('text'):
return item['text'].strip()
# Case 3: Response only has tool calls, no text content
elif hasattr(response, 'tool_calls') and response.tool_calls:
return "Generating your visualization..."
# Case 4: Empty or None content
else:
return "Processing your request..."
except Exception as e:
print(f"Error extracting message content: {str(e)}")
return "I encountered an issue processing your request."
def create_workflow(self) -> StateGraph:
"""Create the DroneBot workflow"""
print("Creating DroneBot workflow")
# Create state graph
workflow = StateGraph(State)
def drone_bot(state: State):
"""Main chatbot that handles plotting requests"""
current_model_config = self.model_manager.get_current_model()
# Add retry logic with model rotation
max_retries = 3
last_error = None
for attempt in range(max_retries):
try:
llm = self.model_manager.create_llm_instance(current_model_config)
print(f"Using model: {current_model_config['name']} ({current_model_config['provider']}) - Attempt {attempt + 1}")
# Bind tools to the LLM
llm_with_tools = llm.bind_tools(self.tools)
# Get messages from state
messages = state["messages"]
# Add system message if not present
if not messages or not isinstance(messages[0], SystemMessage):
system_prompt = prompt_manager.get_prompt("booking")
messages = [SystemMessage(content=system_prompt)] + messages
print(f"DroneBot input messages: {len(messages)}")
# Get response from LLM
response = llm_with_tools.invoke(messages)
print(f"DroneBot response: {type(response).__name__}")
if hasattr(response, 'tool_calls'):
print(f" Tool calls: {len(response.tool_calls) if response.tool_calls else 0}")
# Extract and store the final message content
final_message_content = self._extract_final_message_content(response)
self.final_message = final_message_content
# Update state
updated_state = {"messages": state["messages"] + [response]}
updated_state["current_model"] = current_model_config["name"]
self.final_model_used = current_model_config["name"]
return updated_state
except Exception as e:
last_error = e
print(f"Attempt {attempt + 1} failed with model {current_model_config['name']}: {str(e)}")
if attempt < max_retries - 1:
current_model_config = self.model_manager.rotate_on_failure(current_model_config["name"])
time.sleep(1)
# If all retries failed, raise the last error
raise last_error
def route_tools(state: State):
"""Route to tools if the last message has tool calls"""
messages = state.get("messages", [])
if not messages:
return END
ai_message = messages[-1]
if hasattr(ai_message, "tool_calls") and ai_message.tool_calls:
return "tools"
return END
# Tool execution node
class ToolNode:
"""A node that runs the plotting tools"""
def __init__(self, tools: list, dronebot_instance) -> None:
self.tools_by_name = {tool.name: tool for tool in tools}
self.dronebot = dronebot_instance
def __call__(self, inputs: dict):
messages = inputs.get("messages", [])
if not messages:
raise ValueError("No message found in input")
message = messages[-1]
outputs = []
if not hasattr(message, "tool_calls") or not message.tool_calls:
print("No tool calls found in message")
return {"messages": messages}
print(f"Processing {len(message.tool_calls)} tool calls")
for tool_call in message.tool_calls:
tool_name = tool_call["name"]
tool_args = tool_call["args"]
tool_call_id = tool_call["id"]
print(f"Executing tool: {tool_name}")
try:
if tool_name in self.tools_by_name:
tool_result = self.tools_by_name[tool_name].invoke(tool_args)
else:
raise ValueError(f"Tool {tool_name} not found")
print(f"Tool {tool_name} executed successfully")
# Create tool message
tool_message = ToolMessage(
content=json.dumps(tool_result),
name=tool_name,
tool_call_id=tool_call_id,
)
outputs.append(tool_message)
except Exception as e:
print(f"Error executing tool {tool_name}: {str(e)}")
error_result = {
"status": "error",
"error": str(e),
"tool_name": tool_name
}
error_message = ToolMessage(
content=json.dumps(error_result),
name=tool_name,
tool_call_id=tool_call_id,
)
outputs.append(error_message)
result = {"messages": messages + outputs}
print(f"ToolNode returning {len(outputs)} tool messages")
return result
tool_node = ToolNode(tools=self.tools, dronebot_instance=self)
# Add nodes
workflow.add_node("chatbot", drone_bot)
workflow.add_node("tools", tool_node)
# Add edges
workflow.add_edge(START, "chatbot")
workflow.add_conditional_edges(
"chatbot",
route_tools,
{"tools": "tools", END: END},
)
workflow.add_edge("tools", "chatbot")
# Compile with memory
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
print("DroneBot workflow created successfully")
return app
def convert_to_langchain_messages(self, messages: List[Message]) -> List[HumanMessage | AIMessage]:
"""Convert Message objects to LangChain message objects"""
langchain_messages = []
for msg in messages:
if msg.role == "human" or msg.role == "user":
langchain_messages.append(HumanMessage(content=msg.content))
elif msg.role == "ai" or msg.role == "assistant":
langchain_messages.append(AIMessage(content=msg.content))
return langchain_messages
def chat(self, user_query: str) -> Dict[str, Any]:
"""Main method to interact with DroneBot"""
print(f"DroneBot processing query: {user_query}")
conversation_id = f"dronebot_{int(time.time())}"
config = {"configurable": {"thread_id": conversation_id}}
app = self.create_workflow()
# Prepare input messages
input_messages = []
# Add history if available
if self.history:
input_messages.extend(self.convert_to_langchain_messages(self.history))
# Add current query
input_messages.append(HumanMessage(content=user_query))
# Initialize state
initial_state = {
"messages": input_messages,
"final_response": None,
"user_question": user_query,
"current_model": self.model_manager.get_current_model()["name"]
}
try:
output = app.invoke(initial_state, config)
print("DroneBot workflow completed successfully")
# Ensure final_message is properly set
if not self.final_message:
messages = output.get("messages", [])
for message in reversed(messages):
if isinstance(message, AIMessage):
self.final_message = self._extract_final_message_content(message)
break
if not self.final_message:
self.final_message = "I've processed your visualization request."
final_response = {
"messages": output.get("messages", []),
"final_message": self.final_message,
"final_model_used": self.final_model_used or output.get("current_model", "unknown"),
"user_question": user_query
}
print(f"Final message: {self.final_message[:100]}...")
return final_response
except Exception as e:
print(f"Error in DroneBot workflow execution: {str(e)}")
return {
"messages": [],
"final_message": "Sorry, I encountered an error while processing your visualization request. Please try again.",
"final_model_used": "error",
"user_question": user_query,
"error": str(e)
}
# Example usage
if __name__ == "__main__":
# Example conversation history
history_list = [
{"role": "human", "content": "Hello DroneBot!"},
{"role": "ai", "content": "Hello! How can I help you today?"}
]
history = [Message(role=msg["role"], content=msg["content"]) for msg in history_list]
# Initialize DroneBot
bot = DroneBot(history=history, use_openai_as_fallback=True)
query = "Can we start"
# Chat with DroneBot
response = bot.chat(query)
print("Response:", response["final_message"])
+5
View File
@@ -0,0 +1,5 @@
from .booking_calendar import get_booking_calendar
class AgenTools:
tools = [get_booking_calendar]
Binary file not shown.
+32
View File
@@ -0,0 +1,32 @@
from typing import Dict
from langchain_core.tools import tool
from datetime import datetime
@tool
def get_booking_calendar() -> Dict:
"""
Retrieve the current booking calendar data. This could be integrated with a real calendar API.
"""
try:
# Simulated booking calendar data
calendar_data = {
"status": "success",
"data": {
"available_slots": [
{"date": "2025-07-24", "time": "10:00 AM"},
{"date": "2025-07-24", "time": "2:00 PM"},
{"date": "2025-07-25", "time": "9:30 AM"}
],
"timezone": "UTC+1",
"generated_at": datetime.utcnow().isoformat() + "Z"
}
}
return calendar_data
except Exception as e:
print(f"General Exception: {e}")
return {
"status": "error",
"error": str(e),
}
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+24
View File
@@ -0,0 +1,24 @@
import inspect
class PromptManager:
def __init__(self):
self.prompt_functions = {}
def register_prompt(self, name, prompt_function):
self.prompt_functions[name] = prompt_function
def get_prompt(self, name, **kwargs):
if name not in self.prompt_functions:
raise ValueError(f"Prompt '{name}' not found.")
return self.prompt_functions[name](**kwargs)
def get_prompt_args(self, name):
if name not in self.prompt_functions:
raise ValueError(f"Prompt '{name}' not found.")
func = self.prompt_functions[name]
sig = inspect.signature(func)
return list(sig.parameters.keys())
#resgu
+8
View File
@@ -0,0 +1,8 @@
# setup_prompts.py
from .manager import PromptManager
from src.prompts.templates.chat_templates import get_booking_prompt
def setup_prompt_manager():
pm = PromptManager()
pm.register_prompt("booking", get_booking_prompt)
return pm
View File
+226
View File
@@ -0,0 +1,226 @@
def get_booking_prompt():
return """
## System Instructions & Persona
You are DroneBot, a professional and knowledgeable drone survey booking assistant working for a leading renewable energy inspection company. You have extensive experience in the renewable energy sector and understand the critical importance of regular asset inspections for solar farms, wind turbines, and other renewable energy installations.
Your personality is friendly yet professional, efficient yet thorough. You take pride in helping facility managers, site operators, and maintenance teams schedule high-quality drone inspections that keep their renewable energy assets operating at peak performance. You understand that downtime costs money, so you work quickly to get surveys scheduled while ensuring all safety protocols and requirements are properly addressed.
You are detail-oriented and safety-conscious, always ensuring that our certified drone engineers have all the information they need to conduct safe, effective inspections. You're also resourceful - when challenges arise, you find solutions and alternatives to meet our clients' needs.
Your primary function is to guide users through a comprehensive booking process, collecting all necessary information to schedule drone inspections with our certified engineers. You follow a specific step-by-step process to ensure no critical details are missed.
## Initial Greeting & Introduction
**Bot:** "Hello! I'm your drone survey booking assistant. I'll help you schedule a drone inspection with one of our certified engineers. This will take just a few minutes - I'll ask you some questions about your site and requirements. Let's get started!"
---
## Step 1: Asset Type Identification
**Bot:** "First, what type of asset needs surveying?
Please select:
1. Solar Farm
2. Wind Turbine
3. Other renewable energy asset
If you selected 'Other', please specify what type of asset it is."
**Follow-up if needed:** "Could you provide more details about the asset type? This helps us assign the right specialist engineer."
---
## Step 2: Site Identification
**Bot:** "Great! Now I need some basic site information.
What's the name or identifier for this site? (This could be a site name, project code, or any reference you use)"
**Follow-up:** "Perfect. Could you also provide the location? Please share:
- Full address, OR
- GPS coordinates, OR
- Nearest landmark/town if exact address isn't available"
---
## Step 3: System Size/Scope
**Bot:** "To help our engineer prepare properly, what's the size/scope of your installation?
For example:
- Solar: Number of panels or total kW/MW capacity
- Wind: Number of turbines or total MW capacity
- Other: Any relevant size/capacity details"
**If unclear:** "Any rough estimate is fine - this helps our engineer know what equipment to bring and how long the survey might take."
---
## Step 4: Access Requirements Check
**Bot:** "Now I need to understand site access requirements.
Is your site gated or has restricted access?"
**If YES:**
"I'll need access details:
- Gate code (if applicable)
- Key holder contact details
- Any specific access instructions
- Best entry point or directions"
**If NO:**
"Great! Is the site easily accessible by vehicle for our drone equipment?"
---
## Step 5: On-Site Contact Information
**Bot:** "Who should our engineer contact on the day of the survey?
Please provide:
- Contact person's name
- Phone number
- Their role (optional - e.g., site manager, maintenance technician)"
**Follow-up:** "Will this person be available on-site during the survey, or should our engineer call ahead?"
---
## Step 6: Special Access/Safety Requirements
**Bot:** "Are there any special requirements our engineer should know about?
For example:
- Specific PPE needed
- Safety induction required
- Time restrictions (e.g., only accessible during certain hours)
- Security clearance needed
- Any site hazards or restrictions"
**If none:** "That's fine - our engineers always bring standard safety equipment."
---
## Step 7: Survey Purpose
**Bot:** "What's the main purpose of this drone survey?
Please select:
1. Routine maintenance inspection
2. Insurance assessment
3. Fault diagnosis/investigation
4. Compliance/regulatory requirement
5. Performance optimization
6. Other (please specify)"
**Follow-up based on selection:**
- **Maintenance:** "Any specific areas of concern or components to focus on?"
- **Insurance:** "Do you have any specific requirements from your insurer?"
- **Fault:** "Can you describe the issue you're experiencing?"
- **Compliance:** "Which standards or regulations need to be met?"
---
## Step 8: Special Notes/Additional Requirements
**Bot:** "Almost done! Are there any other details, special requests, or concerns you'd like our engineer to know about?
For example:
- Specific weather requirements
- Preferred time of day
- Areas to avoid
- Additional documentation needed
- Urgent timeline requirements"
**If none:** "No problem - we'll proceed with standard survey procedures."
---
## Step 9: Preferred Timing
**Bot:** "When would you prefer the survey to take place?
Please let me know:
- Any specific dates you prefer
- Days of the week that work best
- Time of day preferences
- How urgent this is (ASAP, within 1 week, within 1 month, flexible)
Or simply tell me if you're flexible and want to see all available options."
---
## Step 10: Calendar Check & Booking Confirmation
**Bot:** "Perfect! Let me check our engineer availability based on your location and requirements...
[SYSTEM: Check available calendar slots based on location, engineer expertise, and user preferences using the approriate tools]
Here are the available time slots:
**Next Week:**
- Monday, [Date] - 9:00 AM - 12:00 PM
- Wednesday, [Date] - 2:00 PM - 5:00 PM
- Friday, [Date] - 10:00 AM - 1:00 PM
**Following Week:**
- Tuesday, [Date] - 8:00 AM - 11:00 AM
- Thursday, [Date] - 1:00 PM - 4:00 PM
Which slot works best for you?"
---
## Final Confirmation
**Bot:** "Excellent! Let me confirm your booking:
**Survey Details:**
- Asset: [Asset Type] at [Site Name]
- Location: [Address/GPS]
- Date & Time: [Selected Slot]
- Purpose: [Survey Purpose]
- Engineer: [Engineer Name] - [Phone Number]
**Your Contact:** [Contact Person] - [Phone Number]
**Access Details:** [Summary of access requirements]
**Special Notes:** [Any special requirements]
This booking is now confirmed! You'll receive a confirmation email shortly.
Our engineer will call your on-site contact 30 minutes before arrival. If you need to make any changes, please contact us at [contact details].
Is there anything else I can help you with today?"
---
## Error Handling & Clarification Prompts
### If User Provides Incomplete Information:
**Bot:** "I need a bit more information to complete your booking. Could you please provide [specific missing detail]?"
### If No Available Slots Match Preferences:
**Bot:** "I don't see any available slots that match your exact preferences. However, I have these alternatives:
[List alternative slots]
Would any of these work, or would you prefer me to check with our scheduling team for other options?"
### If User Wants to Modify Information:
**Bot:** "No problem! Which detail would you like to change? I can update:
1. Site information
2. Access details
3. Survey requirements
4. Contact information
5. Preferred timing"
### If Technical Issues:
**Bot:** "I'm having trouble accessing our calendar system right now. Let me take your details and have our scheduling team contact you within 2 hours to confirm your appointment. Is that acceptable?"
---
## Additional Context for Bot Behavior
- **Tone:** Professional but friendly, efficient but not rushed
- **Flexibility:** Always offer alternatives if first options don't work
- **Confirmation:** Summarize key details at each major step
- **Safety Focus:** Always emphasize safety requirements and proper preparation
- **Contact:** Provide clear next steps and contact information
- **Urgency Handling:** Prioritize urgent requests and escalate when needed
NOTE: THIS IS SOLELY YOUR TASK PLEASE, NO OTHR THING
: DO NOT FABRICATE AVAILABLE SLOTS, ALWAYS CHECK FIRST
"""
View File
+44
View File
@@ -0,0 +1,44 @@
import os
project_structure = {
"README.md": "",
"requirements.txt": "",
"pyproject.toml": "",
".env.example": "",
".gitignore": "",
"Dockerfile": "",
"src/__init__.py": "",
"src/config/__init__.py": "",
"src/config/llm_config.py": "",
"src/prompts/__init__.py": "",
"src/prompts/manager.py": "",
"src/prompts/templates/__init__.py": "",
"src/prompts/templates/chat_templates.py": "",
"src/prompts/validation/__init__.py": "",
"src/prompts/validation/prompt_validator.py": "",
"src/llm/__init__.py": "",
"src/llm/clients/__init__.py": "",
"src/llm/clients/openai_client.py": "",
"src/llm/orchestrator.py": "",
"src/chains/__init__.py": "",
"src/chains/base_chain.py": "",
"src/agents/__init__.py": "",
"src/agents/base_agent.py": "",
"src/evaluation/__init__.py": "",
"src/evaluation/prompt_evaluator.py": "",
"scripts/evaluate_prompts.py": "",
"tests/__init__.py": "",
"tests/unit/test_prompt_manager.py": "",
}
def create_structure(base_path="."):
for path, content in project_structure.items():
full_path = os.path.join(base_path, path)
dir_path = os.path.dirname(full_path)
os.makedirs(dir_path, exist_ok=True)
with open(full_path, "w") as f:
f.write(content)
print("✅ Project structure generated successfully.")
if __name__ == "__main__":
create_structure()
+38
View File
@@ -0,0 +1,38 @@
# terminal_chat.py
from src.llm.orchestrator import DroneBot, Message # Adjust import path as needed
def terminal_chat():
print("🚁 DroneBot Terminal Chat")
print("Type 'exit' to quit.\n")
# Initial conversation history
history = []
# Initialize the bot
bot = DroneBot(history=[], use_openai_as_fallback=True)
# Get initial user input
while True:
user_input = input("👤 You: ")
if user_input.lower() in ("exit", "quit"):
print("👋 Exiting DroneBot. Goodbye!")
break
# Add user message to history
history.append(Message(role="human", content=user_input))
# Update bot's history
bot.history = history
# Get bot response
response = bot.chat(user_input)
# Add bot response to history
history.append(Message(role="ai", content=response["final_message"]))
# Print bot response
print(f"🤖 DroneBot: {response['final_message']}\n")
if __name__ == "__main__":
terminal_chat()
View File
+38
View File
@@ -0,0 +1,38 @@
# terminal_chat.py
from src.llm.orchestrator import DroneBot, Message # Adjust import path as needed
def terminal_chat():
print("🚁 DroneBot Terminal Chat")
print("Type 'exit' to quit.\n")
# Initial conversation history
history = []
# Initialize the bot
bot = DroneBot(history=[], use_openai_as_fallback=True)
# Get initial user input
while True:
user_input = input("👤 You: ")
if user_input.lower() in ("exit", "quit"):
print("👋 Exiting DroneBot. Goodbye!")
break
# Add user message to history
history.append(Message(role="human", content=user_input))
# Update bot's history
bot.history = history
# Get bot response
response = bot.chat(user_input)
# Add bot response to history
history.append(Message(role="ai", content=response["final_message"]))
# Print bot response
print(f"🤖 DroneBot: {response['final_message']}\n")
if __name__ == "__main__":
terminal_chat()
View File