β π Complete Function Flow
Input: "Create an order for John Doe at 123 Main St, San Francisco"
Let me trace every single function call with this exact example!
π FUNCTION CALL SEQUENCE:
STEP 1: User Clicks Send Button
FILE: ui/app.py LINE: 448-452
User clicks "Send" button in Gradio UI
β
FUNCTION CALLED: send_message(message, conv_state)
message = "Create an order for John Doe at 123 Main St, San
Francisco"
conv_state = ConversationManager object
Function Code:
def send_message(message, conv_state):
"""Handle send button click"""
chat_history, tools, new_state = handle_chat_message(message,
conv_state)
# β
# CALLS THIS NEXT
return chat_history, tools, new_state, ""
STEP 2: handle_chat_message()
FILE: ui/app.py LINE: 223-241
FUNCTION: handle_chat_message(message, conversation_state)
message = "Create an order for John Doe at 123 Main St, San
Francisco"
conversation_state = ConversationManager object
Function Code: def handle_chat_message(message, conversation_state): if not message.strip(): return ...
# Process message through chat engine
response, tool_calls = chat_engine.process_message(message,
conversation_state) # β # CALLS THIS NEXT
# Return updated UI
return conversation_state.get_formatted_history(),
conversation_state.get_tool_calls(), conversation_state
STEP 3: chat_engine.process_message()
FILE: chat/chat_engine.py LINE: 58-73
FUNCTION: ChatEngine.process_message(user_message, conversation)
user_message = "Create an order for John Doe at 123 Main St,
San Francisco"
conversation = ConversationManager object
Function Code:
def process_message(self, user_message, conversation):
"""Process user message and return AI response"""
return self.provider.process_message(user_message,
conversation)
# β
# self.provider = GeminiProvider (from chat_engine.py:26)
# CALLS GeminiProvider.process_message() NEXT
STEP 4: GeminiProvider.process_message()
FILE: chat/providers/gemini_provider.py LINE: 173-212
FUNCTION: GeminiProvider.process_message(user_message,
conversation)
user_message = "Create an order for John Doe at 123 Main St,
San Francisco"
conversation = ConversationManager object
Function Code: def process_message(self, user_message, conversation): """Process user message with Gemini""" if not self.api_available: return self._handle_no_api(), []
# Lazy initialization on first use
self._ensure_initialized() # β CALLS THIS if not initialized
if not self._initialized:
return "β οΈ Failed to initialize...", []
try:
# Build conversation history for Gemini
chat =
self.model.start_chat(history=self._convert_history(conversation))
# β
# CALLS
_convert_history()
# Send message and get response
response = chat.send_message(user_message,
safety_settings={...})
# β
# π API CALL TO GOOGLE GEMINI
# Sends: "Create an order for John Doe at 123
Main St, San Francisco"
# Add user message to conversation
conversation.add_message("user", user_message)
# Process response and handle function calls
return self._process_response(response, conversation,
chat) # β # CALLS THIS NEXT
STEP 5: Gemini API Processes Request
π GOOGLE GEMINI API (External)
RECEIVES:
- System Prompt: "You are an AI assistant for FleetMind..."
- User Message: "Create an order for John Doe at 123 Main St, San
Francisco" - Available Tools: [geocode_address, create_order]
AI ANALYZES: "User wants to create an order. I have: β Customer Name: John Doe β Address: 123 Main St, San Francisco β GPS Coordinates: Missing!
DECISION: Call geocode_address tool first to get coordinates."
RETURNS TO CODE:
response = {
candidates: [{
content: {
parts: [{
function_call: {
name: "geocode_address",
args: {
"address": "123 Main St, San Francisco"
}
}
}]
}
}]
}
STEP 6: _process_response() - Detects Function Call
FILE: chat/providers/gemini_provider.py LINE: 226-393
FUNCTION: _process_response(response, conversation, chat) response = Response from Gemini with function_call conversation = ConversationManager object chat = Gemini chat session
Function Code: def _process_response(self, response, conversation, chat): """Process Gemini's response and handle function calls""" tool_calls_made = []
try:
# Check ALL parts for function calls
parts = response.candidates[0].content.parts
logger.info(f"Processing response with {len(parts)}
part(s)") # β # LOGS: "Processing response with 1 part(s)"
for part in parts:
if hasattr(part, 'function_call'):
fc = part.function_call
if fc and hasattr(fc, 'name') and fc.name:
has_function_call = True
logger.info(f"Detected function call:
{fc.name}")
# β
# LOGS: "Detected function call:
geocode_address"
break
if has_function_call:
# Handle function calls (potentially multiple in
sequence) current_response = response max_iterations = 10
for iteration in range(max_iterations): # β LOOP
STARTS # Extract function call details first_part = current_response.candidates[0].content.parts[0] function_call = first_part.function_call function_name = function_call.name # "geocode_address" function_args = dict(function_call.args) # {"address": "123 Main St, San Francisco"}
logger.info(f"Gemini executing function:
{function_name} (iteration {iteration + 1})")
# β
# LOGS: "Gemini executing function:
geocode_address (iteration 1)"
# Execute the tool
tool_result = execute_tool(function_name,
function_args) # β # CALLS execute_tool() NEXT
STEP 7: execute_tool() - Routes to Handler
FILE: chat/tools.py LINE: 92-118
FUNCTION: execute_tool(tool_name, tool_input) tool_name = "geocode_address" tool_input = {"address": "123 Main St, San Francisco"}
Function Code:
def execute_tool(tool_name, tool_input):
"""Route tool execution to appropriate handler"""
try:
if tool_name == "geocode_address":
return handle_geocode_address(tool_input)
# β
# CALLS THIS NEXT
elif tool_name == "create_order":
return handle_create_order(tool_input)
else:
return {"success": False, "error": f"Unknown tool:
{tool_name}"}
except Exception as e:
logger.error(f"Tool execution error ({tool_name}): {e}")
return {"success": False, "error": str(e)}
STEP 8: handle_geocode_address()
FILE: chat/tools.py LINE: 121-150
FUNCTION: handle_geocode_address(tool_input) tool_input = {"address": "123 Main St, San Francisco"}
Function Code:
def handle_geocode_address(tool_input):
"""Execute geocoding tool"""
address = tool_input.get("address", "") # "123 Main St, San
Francisco"
if not address:
return {"success": False, "error": "Address is required"}
logger.info(f"Geocoding address: {address}")
# β
# LOGS: "Geocoding address: 123 Main St, San
Francisco"
result = geocoding_service.geocode(address)
# β
# CALLS geocoding_service.geocode() NEXT
return {
"success": True,
"latitude": result["lat"],
"longitude": result["lng"],
"formatted_address": result["formatted_address"],
"confidence": result["confidence"],
"message": f"Address geocoded successfully
({result['confidence']})" }
STEP 9: GeocodingService.geocode()
FILE: chat/geocoding.py LINE: 28-65
FUNCTION: GeocodingService.geocode(address) address = "123 Main St, San Francisco"
Function Code: def geocode(self, address): """Geocode an address to coordinates""" if not address: return self._error_response("Address is required")
# Use mock or real API
if self.use_mock: # True (no HERE_API_KEY configured)
return self._geocode_mock(address)
# β
# CALLS THIS NEXT
else:
return self._geocode_here(address)
STEP 10: _geocode_mock() - Returns Coordinates
FILE: chat/geocoding.py LINE: 52-70
FUNCTION: _geocode_mock(address) address = "123 Main St, San Francisco"
Function Code: def _geocode_mock(self, address): """Mock geocoding using city detection""" address_lower = address.lower()
# Try to detect city in address
for city_name, (lat, lng) in CITY_COORDINATES.items():
if city_name in address_lower:
logger.info(f"Mock geocoding detected city:
{city_name}")
# β
# LOGS: "Mock geocoding detected city: san
francisco"
return {
"lat": lat, # 37.7749
"lng": lng, # -122.4194
"formatted_address": address,
"confidence": "mock"
}
# Default to San Francisco if no city detected
return {
"lat": 37.7749,
"lng": -122.4194,
"formatted_address": address,
"confidence": "mock"
}
RETURNS: { "lat": 37.7749, "lng": -122.4194, "formatted_address": "123 Main St, San Francisco", "confidence": "mock" }
STEP 11: Back to handle_geocode_address()
FILE: chat/tools.py LINE: 141-150
result = { "lat": 37.7749, "lng": -122.4194, "formatted_address": "123 Main St, San Francisco", "confidence": "mock" }
RETURNS: { "success": True, "latitude": 37.7749, "longitude": -122.4194, "formatted_address": "123 Main St, San Francisco", "confidence": "mock", "message": "Address geocoded successfully (mock)" }
STEP 12: Back to _process_response() - Tool Result Received
FILE: chat/providers/gemini_provider.py LINE: 285-310
tool_result = { "success": True, "latitude": 37.7749, "longitude": -122.4194, "formatted_address": "123 Main St, San Francisco", "confidence": "mock", "message": "Address geocoded successfully (mock)" }
Track for transparency
tool_calls_made.append({ "tool": "geocode_address", "input": {"address": "123 Main St, San Francisco"}, "result": tool_result })
conversation.add_tool_result("geocode_address", function_args,
tool_result)
Send function result back to Gemini
current_response = chat.send_message( genai.protos.Content( parts=[genai.protos.Part( function_response=genai.protos.FunctionResponse( name="geocode_address", response={"result": tool_result} ) )] ) )
β
π API CALL TO GEMINI WITH GEOCODING RESULT
STEP 13: Gemini Receives Geocoding Result
π GOOGLE GEMINI API (External)
RECEIVES:
- Function: geocode_address
- Result: { "success": True, "latitude": 37.7749, "longitude": -122.4194 }
AI ANALYZES: "Great! I now have GPS coordinates: β Customer Name: John Doe β Address: 123 Main St, San Francisco β Latitude: 37.7749 β Longitude: -122.4194
DECISION: Now I can create the order in the database! Call create_order tool."
RETURNS TO CODE: response = { candidates: [{ content: { parts: [{ function_call: { name: "create_order", args: { "customer_name": "John Doe", "delivery_address": "123 Main St, San Francisco", "delivery_lat": 37.7749, "delivery_lng": -122.4194, "priority": "standard" } } }] } }] }
STEP 14: Loop Continues - Detects create_order
FILE: chat/providers/gemini_provider.py LINE: 252-285
Still in the for loop (iteration 2)
first_part = current_response.candidates[0].content.parts[0] has_fc = True # Another function call detected
function_call = first_part.function_call function_name = function_call.name # "create_order" function_args = dict(function_call.args) # {customer_name, address, lat, lng...}
logger.info(f"Gemini executing function: {function_name} (iteration 2)")
β
LOGS: "Gemini executing function: create_order
(iteration 2)"
Execute the tool
tool_result = execute_tool(function_name, function_args)
β
CALLS execute_tool() AGAIN
STEP 15: execute_tool() - Routes to create_order
FILE: chat/tools.py LINE: 92-118
FUNCTION: execute_tool(tool_name, tool_input) tool_name = "create_order" tool_input = { "customer_name": "John Doe", "delivery_address": "123 Main St, San Francisco", "delivery_lat": 37.7749, "delivery_lng": -122.4194, "priority": "standard" }
Function Code: def execute_tool(tool_name, tool_input): try: if tool_name == "geocode_address": return handle_geocode_address(tool_input) elif tool_name == "create_order": return handle_create_order(tool_input) # β # CALLS THIS NEXT
STEP 16: handle_create_order()
FILE: chat/tools.py LINE: 153-242
FUNCTION: handle_create_order(tool_input) tool_input = { "customer_name": "John Doe", "delivery_address": "123 Main St, San Francisco", "delivery_lat": 37.7749, "delivery_lng": -122.4194, "priority": "standard" }
Function Code: def handle_create_order(tool_input): """Execute order creation tool"""
# Extract fields with defaults
customer_name = tool_input.get("customer_name") # "John Doe"
customer_phone = tool_input.get("customer_phone") # None
customer_email = tool_input.get("customer_email") # None
delivery_address = tool_input.get("delivery_address") # "123
Main St, San Francisco"
delivery_lat = tool_input.get("delivery_lat") # 37.7749
delivery_lng = tool_input.get("delivery_lng") # -122.4194
priority = tool_input.get("priority", "standard") #
"standard"
special_instructions = tool_input.get("special_instructions")
None
weight_kg = tool_input.get("weight_kg", 5.0) # 5.0
# Validate required fields
if not all([customer_name, delivery_address, delivery_lat,
delivery_lng]): return {"success": False, "error": "Missing required fields..."}
# Generate order ID
now = datetime.now()
order_id = f"ORD-{now.strftime('%Y%m%d%H%M%S')}"
# β
# e.g., "ORD-20251114015858"
# Handle time window
time_window_end = now + timedelta(hours=6) # 6 hours from now
time_window_start = now + timedelta(hours=2) # 2 hours from
now
# Insert into database
query = """
INSERT INTO orders (
order_id, customer_name, customer_phone,
customer_email,
delivery_address, delivery_lat, delivery_lng,
time_window_start, time_window_end,
priority, weight_kg, status, special_instructions
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s)
"""
params = (
order_id, # "ORD-20251114015858"
customer_name, # "John Doe"
customer_phone, # None
customer_email, # None
delivery_address, # "123 Main St, San Francisco"
delivery_lat, # 37.7749
delivery_lng, # -122.4194
time_window_start, # 2025-11-14 03:58:58
time_window_end, # 2025-11-14 07:58:58
priority, # "standard"
weight_kg, # 5.0
"pending", # status
special_instructions # None
)
try:
execute_write(query, params)
# β
# CALLS THIS NEXT - DATABASE WRITE!
STEP 17: execute_write() - INSERT INTO DATABASE
FILE: database/connection.py LINE: 71-97
FUNCTION: execute_write(query, params)
query = "INSERT INTO orders (...) VALUES (%s, %s, ...)"
params = ("ORD-20251114015858", "John Doe", None, None, "123
Main St...", ...)
Function Code: def execute_write(query, params=None): """Execute a write query (INSERT, UPDATE, DELETE)""" try: # Connect to PostgreSQL conn = get_db_connection() # β # Opens connection to localhost:5432/fleetmind
logger.info("Database connection established:
fleetmind@localhost")
cursor = conn.cursor()
# Execute INSERT query
cursor.execute(query, params)
# β
# πΎ EXECUTES SQL:
# INSERT INTO orders (order_id, customer_name, ...)
# VALUES ('ORD-20251114015858', 'John Doe', ...)
conn.commit() # β SAVES TO POSTGRESQL PERMANENTLY!
rows_affected = cursor.rowcount # 1
cursor.close()
conn.close()
logger.info("Database connection closed")
return rows_affected # Returns 1
DATABASE STATE: -- New row added to orders table: ORDER_ID: ORD-20251114015858 CUSTOMER_NAME: John Doe CUSTOMER_PHONE: NULL CUSTOMER_EMAIL: NULL DELIVERY_ADDRESS: 123 Main St, San Francisco DELIVERY_LAT: 37.7749 DELIVERY_LNG: -122.4194 STATUS: pending PRIORITY: standard WEIGHT_KG: 5.0 CREATED_AT: 2025-11-14 01:58:58
STEP 18: Back to handle_create_order() - Success!
FILE: chat/tools.py LINE: 224-242
execute_write(query, params) # Returned 1 (success)
logger.info(f"Order created: {order_id}")
β
LOGS: "Order created: ORD-20251114015858"
return {
"success": True,
"order_id": "ORD-20251114015858",
"status": "pending",
"customer": "John Doe",
"address": "123 Main St, San Francisco",
"deadline": "2025-11-14 07:58",
"priority": "standard",
"message": "Order ORD-20251114015858 created successfully!"
}
STEP 19: Back to _process_response() - Second Tool Complete
FILE: chat/providers/gemini_provider.py LINE: 285-310
tool_result = { "success": True, "order_id": "ORD-20251114015858", "status": "pending", "customer": "John Doe", ... }
Track for transparency
tool_calls_made.append({ "tool": "create_order", "input": { "customer_name": "John Doe", "delivery_address": "123 Main St, San Francisco", "delivery_lat": 37.7749, "delivery_lng": -122.4194 }, "result": tool_result })
conversation.add_tool_result("create_order", function_args, tool_result)
Send function result back to Gemini
current_response = chat.send_message( genai.protos.Content( parts=[genai.protos.Part( function_response=genai.protos.FunctionResponse( name="create_order", response={"result": tool_result} ) )] ) )
β
π API CALL TO GEMINI WITH ORDER CREATION RESULT
STEP 20: Gemini Generates Final Response
π GOOGLE GEMINI API (External)
RECEIVES:
- Function: create_order
- Result: { "success": True, "order_id": "ORD-20251114015858", "customer": "John Doe", "address": "123 Main St, San Francisco" }
AI ANALYZES: "Perfect! Order successfully created in database! Let me tell the user with a nice summary."
GENERATES TEXT RESPONSE: "Awesome! I have created the order for John Doe.
Here's a summary: β’ Order ID: ORD-20251114015858 β’ Customer: John Doe β’ Address: 123 Main St, San Francisco β’ Status: Pending β’ Priority: Standard
The order has been successfully saved to the database!"
RETURNS TO CODE:
response = {
candidates: [{
content: {
parts: [{
text: "Awesome! I have created the order for John
Doe.\n\nHere's a summary:..."
}]
}
}]
}
STEP 21: _process_response() - Extract Final Text
FILE: chat/providers/gemini_provider.py LINE: 272-356
Loop detects no more function calls
logger.info(f"No more function calls after iteration 2")
Extract text from final response
parts = current_response.candidates[0].content.parts logger.info(f"Extracting text from {len(parts)} parts")
for idx, part in enumerate(parts):
if hasattr(part, 'text') and part.text:
logger.info(f"Part {idx} has text: {part.text[:50]}...")
final_text += part.text
final_text = "Awesome! I have created the order for John Doe..."
logger.info(f"Returning response: {final_text[:100]}")
conversation.add_message("assistant", final_text)
return final_text, tool_calls_made
β β
Response List of 2 tool calls [geocode, create_order]
RETURNS:
(
"Awesome! I have created the order for John Doe.\n\nHere's a
summary:...",
[
{"tool": "geocode_address", "input": {...}, "result":
{...}},
{"tool": "create_order", "input": {...}, "result": {...}}
]
)
STEP 22: Back Through All Functions
β Returns to: GeminiProvider.process_message() (line 206) β Returns to: ChatEngine.process_message() (line 58) β Returns to: handle_chat_message() (line 223) β Returns to: send_message() (line 443) β Returns to: Gradio UI (line 448)
STEP 23: Gradio Updates UI
FILE: ui/app.py LINE: 448-452
send_btn.click(
fn=send_message,
outputs=[chatbot, tool_display, conversation_state, msg_input]
# β β
# Updates Shows tool calls
)
CHATBOT DISPLAYS: User: "Create an order for John Doe at 123 Main St, San Francisco"