Skip to content

Commit c615f17

Browse files
badri-singhalswaroopvarma1
authored andcommitted
Support for address confirmation in breeze buddy
1 parent 6bbbacb commit c615f17

31 files changed

Lines changed: 428 additions & 187 deletions

File tree

.githooks/pre-commit

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/sh
2+
echo "Running Black formatter before commit..."
3+
4+
# Activate virtual environment
5+
source venv/bin/activate
6+
7+
# Ensure dependencies are installed
8+
pip install -r requirements-dev.txt
9+
10+
# Format all python files
11+
python -m black .
12+
13+
# Stage updated files (if black changed them)
14+
git add -u
15+
16+
echo "Black formatting complete."

.pre-commit-config.yaml

Lines changed: 0 additions & 14 deletions
This file was deleted.

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ The project is organized into a main FastAPI application (`app/`) with a clear s
5454

5555
### Prerequisites
5656

57-
* Python 3.8+
57+
* Python 3.10+
5858
* Database (e.g., PostgreSQL)
5959
* Access to required third-party APIs (e.g., Azure OpenAI, Daily.co, Twilio/Exotel) with valid keys.
6060

@@ -70,12 +70,17 @@ The project is organized into a main FastAPI application (`app/`) with a clear s
7070
```bash
7171
pip install -r requirements.txt
7272
```
73-
4. **Set up Environment Variables:**
73+
4. **Run the setup script:**
74+
This script will handle any additional setup required for the project.
75+
```bash
76+
./scripts/setup.sh
77+
```
78+
5. **Set up Environment Variables:**
7479
Create a `.env` file in the project root by copying `.env.example` and filling in the required values for the database, API keys, and other configurations.
75-
5. **Initialize the Database:**
80+
6. **Initialize the Database:**
7681
Run the script to create the necessary tables in your database.
7782
```bash
78-
python -m app.scripts.create_tables
83+
python -m scripts.create_tables create
7984
```
8085

8186
## 5. Running the Server

app/agents/voice/automatic/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,11 @@ async def run_pipeline():
513513
user_id=args.user_email,
514514
session_id=args.session_id,
515515
tags=[
516-
voice_name.value
517-
if hasattr(voice_name, "value")
518-
else str(voice_name)
516+
(
517+
voice_name.value
518+
if hasattr(voice_name, "value")
519+
else str(voice_name)
520+
)
519521
],
520522
)
521523
await run_pipeline()

app/agents/voice/automatic/features/charts/conversation.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,9 @@ def turn_start(cls, session_id: str, turn: ConversationTurn) -> "ConversationEve
319319
"turn_id": turn.id,
320320
"turn_number": turn.turn_number,
321321
"status": turn.status,
322-
"user_message": turn.user_message.model_dump()
323-
if turn.user_message
324-
else None,
322+
"user_message": (
323+
turn.user_message.model_dump() if turn.user_message else None
324+
),
325325
"tool_calls": [], # Empty at start
326326
"tool_results": [], # Empty at start
327327
"tool_count": 0, # Zero at start
@@ -422,8 +422,8 @@ def full_state(
422422
payload={
423423
"conversation": conversation.model_dump(),
424424
"turn_count": len(conversation.turns),
425-
"summary": conversation.summary.model_dump()
426-
if conversation.summary
427-
else None,
425+
"summary": (
426+
conversation.summary.model_dump() if conversation.summary else None
427+
),
428428
},
429429
)

app/agents/voice/automatic/services/mem0/memory.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,11 @@ def _convert_context_messages_to_dict(
217217
dict_messages.append(
218218
{
219219
"role": msg["role"],
220-
"content": str(msg["content"])
221-
if not isinstance(msg["content"], str)
222-
else msg["content"],
220+
"content": (
221+
str(msg["content"])
222+
if not isinstance(msg["content"], str)
223+
else msg["content"]
224+
),
223225
}
224226
)
225227
else:

app/agents/voice/automatic/tools/internet/search.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
tools=tools_list,
2727
)
2828

29+
2930
# ---------- 2. Define a function that uses Gemini to perform web search ----------
3031
async def gemini_search_fn(params: FunctionCallParams):
3132
query = params.arguments.get("query")

app/agents/voice/automatic/tools/juspay/analytics.py

Lines changed: 94 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -710,9 +710,11 @@ async def create_euler_offer(params: FunctionCallParams):
710710
"validTo": end_date,
711711
"minAmount": min_order_amount,
712712
"sponsoredBy": sponsored_by,
713-
"paymentMethods": payment_instruments
714-
if payment_instruments
715-
else "All payment methods",
713+
"paymentMethods": (
714+
payment_instruments
715+
if payment_instruments
716+
else "All payment methods"
717+
),
716718
},
717719
}
718720
await params.result_callback({"data": json.dumps(success_result)})
@@ -1268,22 +1270,32 @@ async def update_euler_offer(params: FunctionCallParams):
12681270
"offer_code": offer_code,
12691271
"batch_id": existing_offer.get("batch_id", ""),
12701272
"offer_description": {
1271-
"title": offer_title
1272-
if offer_title is not None
1273-
else existing_offer.get("offer_description", {}).get("title", ""),
1274-
"description": offer_description
1275-
if offer_description is not None
1276-
else existing_offer.get("offer_description", {}).get("description", ""),
1273+
"title": (
1274+
offer_title
1275+
if offer_title is not None
1276+
else existing_offer.get("offer_description", {}).get("title", "")
1277+
),
1278+
"description": (
1279+
offer_description
1280+
if offer_description is not None
1281+
else existing_offer.get("offer_description", {}).get(
1282+
"description", ""
1283+
)
1284+
),
12771285
"tnc": existing_offer.get("offer_description", {}).get("tnc", ""),
1278-
"sponsored_by": sponsored_by
1279-
if sponsored_by is not None
1280-
else existing_offer.get("offer_description", {}).get(
1281-
"sponsored_by", "BREEZE"
1286+
"sponsored_by": (
1287+
sponsored_by
1288+
if sponsored_by is not None
1289+
else existing_offer.get("offer_description", {}).get(
1290+
"sponsored_by", "BREEZE"
1291+
)
12821292
),
1283-
"display_title": offer_title
1284-
if offer_title is not None
1285-
else existing_offer.get("offer_description", {}).get(
1286-
"display_title", ""
1293+
"display_title": (
1294+
offer_title
1295+
if offer_title is not None
1296+
else existing_offer.get("offer_description", {}).get(
1297+
"display_title", ""
1298+
)
12871299
),
12881300
},
12891301
"ui_configs": existing_offer.get(
@@ -1307,11 +1319,13 @@ async def update_euler_offer(params: FunctionCallParams):
13071319
"max_order_amount": existing_offer.get("rule_dsl", {})
13081320
.get("order", {})
13091321
.get("max_order_amount"),
1310-
"min_order_amount": str(min_order_amount)
1311-
if min_order_amount is not None
1312-
else existing_offer.get("rule_dsl", {})
1313-
.get("order", {})
1314-
.get("min_order_amount", "1"),
1322+
"min_order_amount": (
1323+
str(min_order_amount)
1324+
if min_order_amount is not None
1325+
else existing_offer.get("rule_dsl", {})
1326+
.get("order", {})
1327+
.get("min_order_amount", "1")
1328+
),
13151329
"currency": existing_offer.get("rule_dsl", {})
13161330
.get("order", {})
13171331
.get("currency", "INR"),
@@ -1322,9 +1336,13 @@ async def update_euler_offer(params: FunctionCallParams):
13221336
"additional_payment_filters": existing_offer.get("rule_dsl", {}).get(
13231337
"additional_payment_filters"
13241338
),
1325-
"payment_instrument": payment_instruments_payload
1326-
if payment_instruments_payload is not None
1327-
else existing_offer.get("rule_dsl", {}).get("payment_instrument", []),
1339+
"payment_instrument": (
1340+
payment_instruments_payload
1341+
if payment_instruments_payload is not None
1342+
else existing_offer.get("rule_dsl", {}).get(
1343+
"payment_instrument", []
1344+
)
1345+
),
13281346
"counters": existing_offer.get("rule_dsl", {}).get("counters", []),
13291347
"txn_type": existing_offer.get("rule_dsl", {}).get(
13301348
"txn_type", ["ORDER"]
@@ -1334,29 +1352,37 @@ async def update_euler_offer(params: FunctionCallParams):
13341352
),
13351353
"benefits": [
13361354
{
1337-
"type": offer_type
1338-
if offer_type is not None
1339-
else existing_offer.get("rule_dsl", {})
1340-
.get("benefits", [{}])[0]
1341-
.get("type", "DISCOUNT"),
1342-
"calculation_rule": calculation_type
1343-
if calculation_type is not None
1344-
else existing_offer.get("rule_dsl", {})
1345-
.get("benefits", [{}])[0]
1346-
.get("calculation_rule", "ABSOLUTE"),
1347-
"value": discount_value
1348-
if discount_value is not None
1349-
else existing_offer.get("rule_dsl", {})
1350-
.get("benefits", [{}])[0]
1351-
.get("value", 0),
1355+
"type": (
1356+
offer_type
1357+
if offer_type is not None
1358+
else existing_offer.get("rule_dsl", {})
1359+
.get("benefits", [{}])[0]
1360+
.get("type", "DISCOUNT")
1361+
),
1362+
"calculation_rule": (
1363+
calculation_type
1364+
if calculation_type is not None
1365+
else existing_offer.get("rule_dsl", {})
1366+
.get("benefits", [{}])[0]
1367+
.get("calculation_rule", "ABSOLUTE")
1368+
),
1369+
"value": (
1370+
discount_value
1371+
if discount_value is not None
1372+
else existing_offer.get("rule_dsl", {})
1373+
.get("benefits", [{}])[0]
1374+
.get("value", 0)
1375+
),
13521376
"amount_info": existing_offer.get("rule_dsl", {})
13531377
.get("benefits", [{}])[0]
13541378
.get("amount_info", []),
1355-
"max_amount": max_discount_amount
1356-
if max_discount_amount is not None
1357-
else existing_offer.get("rule_dsl", {})
1358-
.get("benefits", [{}])[0]
1359-
.get("max_amount"),
1379+
"max_amount": (
1380+
max_discount_amount
1381+
if max_discount_amount is not None
1382+
else existing_offer.get("rule_dsl", {})
1383+
.get("benefits", [{}])[0]
1384+
.get("max_amount")
1385+
),
13601386
"global_max_amount": existing_offer.get("rule_dsl", {})
13611387
.get("benefits", [{}])[0]
13621388
.get("global_max_amount"),
@@ -1366,15 +1392,19 @@ async def update_euler_offer(params: FunctionCallParams):
13661392
"filters", {"blacklist": [], "whitelist": []}
13671393
),
13681394
},
1369-
"status": status
1370-
if status is not None
1371-
else existing_offer.get("status", "ACTIVE"),
1372-
"start_time": start_date_iso
1373-
if start_date_iso is not None
1374-
else existing_offer.get("start_time"),
1375-
"end_time": end_date_iso
1376-
if end_date_iso is not None
1377-
else existing_offer.get("end_time"),
1395+
"status": (
1396+
status if status is not None else existing_offer.get("status", "ACTIVE")
1397+
),
1398+
"start_time": (
1399+
start_date_iso
1400+
if start_date_iso is not None
1401+
else existing_offer.get("start_time")
1402+
),
1403+
"end_time": (
1404+
end_date_iso
1405+
if end_date_iso is not None
1406+
else existing_offer.get("end_time")
1407+
),
13781408
"metadata": {
13791409
"analytics_offer_code": offer_code,
13801410
"customerResetPeriodType": existing_offer.get("metadata", {}).get(
@@ -1395,12 +1425,16 @@ async def update_euler_offer(params: FunctionCallParams):
13951425
"productUpiResetPeriodType": existing_offer.get("metadata", {}).get(
13961426
"productUpiResetPeriodType", "offerPeriod"
13971427
),
1398-
"start_date": start_date_iso
1399-
if start_date_iso is not None
1400-
else existing_offer.get("metadata", {}).get("start_date"),
1401-
"end_date": end_date_iso
1402-
if end_date_iso is not None
1403-
else existing_offer.get("metadata", {}).get("end_date"),
1428+
"start_date": (
1429+
start_date_iso
1430+
if start_date_iso is not None
1431+
else existing_offer.get("metadata", {}).get("start_date")
1432+
),
1433+
"end_date": (
1434+
end_date_iso
1435+
if end_date_iso is not None
1436+
else existing_offer.get("metadata", {}).get("end_date")
1437+
),
14041438
},
14051439
"udf1": existing_offer.get("udf1"),
14061440
"udf2": existing_offer.get("udf2"),

app/agents/voice/automatic/types/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ApiFailure:
3737
# A union type to represent either outcome
3838
GeniusApiResponse = Union[ApiSuccess, ApiFailure]
3939

40+
4041
# --- MCP-Compliant Pydantic Models ---
4142
class ToolInputSchema(BaseModel):
4243
type: str = "object"

app/agents/voice/breeze_buddy/managers/calls.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Cron manager for handling background tasks.
33
"""
4+
45
from datetime import datetime, timezone, timedelta
56
from app.core.logger import logger
67
import uuid
@@ -114,7 +115,11 @@ async def process_backlog_leads():
114115

115116

116117
async def handle_call_completion(
117-
call_id: str, outcome: LeadCallOutcome, transcription: dict, call_end_time: datetime
118+
call_id: str,
119+
outcome: LeadCallOutcome,
120+
transcription: dict,
121+
call_end_time: datetime,
122+
updated_address: str | None = None,
118123
):
119124
"""
120125
Handles call completion events.
@@ -143,7 +148,11 @@ async def handle_call_completion(
143148
id=lead.id,
144149
status=LeadCallStatus.FINISHED,
145150
outcome=outcome,
146-
meta_data={"transcription": transcription},
151+
meta_data=(
152+
{"transcription": transcription, "updated_address": updated_address}
153+
if updated_address
154+
else {"transcription": transcription}
155+
),
147156
call_end_time=call_end_time,
148157
)
149158

0 commit comments

Comments
 (0)