-
Notifications
You must be signed in to change notification settings - Fork 52
WIP: Daily support for Buddy #451
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThis pull request introduces Daily transport support for Breeze Buddy voice agent system, mirroring the existing telephony pattern. It adds a new Daily API endpoint, refactors the agent to support transport-agnostic initialization, adds router registration, and introduces related schema models for Daily connect requests and supporting entities. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Endpoint as /api/breeze_buddy/daily
participant DailyService as DailyRESTHelper
participant Agent as Breeze Buddy Agent
participant DailyTransport
Client->>Endpoint: POST /connect (BreezeBuddyDailyConnectRequest)
rect rgb(200, 220, 240)
Note over Endpoint: Setup Phase
Endpoint->>Endpoint: Generate session_id, call_sid
Endpoint->>DailyService: Create room (via API)
DailyService-->>Endpoint: room_url
Endpoint->>DailyService: Get user token
DailyService-->>Endpoint: user_token
Endpoint->>DailyService: Get bot token
DailyService-->>Endpoint: bot_token
end
rect rgb(220, 240, 200)
Note over Endpoint,Agent: Bot Startup (async)
Endpoint->>Endpoint: Assemble DailyRunnerArguments
Endpoint->>Agent: bot(runner_args) [background]
Agent->>Agent: __init__(transport_type="daily")
Agent->>Agent: run(runner_args)
Agent->>Agent: Resolve lead, load template config
Agent->>DailyTransport: create_transport(transport_params)
DailyTransport-->>Agent: Transport ready
end
Endpoint-->>Client: {room_url, user_token, session_id, call_sid}
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/ai/voice/agents/breeze_buddy/agent.py (2)
289-345: Critical: This code block contains syntax errors and duplicated/orphaned logic.This section has multiple critical issues:
- Syntax error at line 320: Static analysis reports "Unexpected indentation" - the code structure is broken
- Undefined variables: Lines 291-306 reference
template,call_payload, andmerchant_idwhich are not defined at this scope- Duplicated logic: Lines 289-307 duplicate the template variable building already done in
_load_template_config()(lines 149-167)- Orphaned code: Lines 310-318 create a VAD analyzer but this is already handled in
transport_params(Daily) or should be inside the telephony blockThis appears to be remnants from an incomplete refactoring. The code from lines 289-345 should likely be removed or properly restructured within the telephony
elseblock.🔎 Suggested fix - remove orphaned code
The logic for
template_varsbuilding andtemplate_configloading is already in_load_template_config(). The VAD and transport creation for telephony should be inside theelseblock. Remove lines 289-318 and ensure proper indentation:try: await self._load_template_config() except ValueError as e: try: if self.ws.client_state.name != "DISCONNECTED": await self.ws.close(code=4000, reason=str(e)) except Exception as close_error: logger.warning( f"Could not close websocket (likely already closed): {close_error}" ) return - # Build template_vars dynamically based on expected_payload_schema - self.template_vars = {} - for field_name in template.expected_payload_schema.keys(): - if field_name in call_payload: - self.template_vars[field_name] = call_payload[field_name] - else: - logger.warning(f"Field '{field_name}' from schema not found in payload") - self.template_vars[field_name] = "" - - logger.info( - f"Dynamically built template_vars from schema: {list(self.template_vars.keys())}" - ) - - self.template_config = await self.flow_loader.load_template( - merchant_id=merchant_id, - template=self.lead.template, - template_vars=self.template_vars, - shop_identifier=self.lead.shop_identifier, - ) - - # Create VAD analyzer and store reference for muting - self.vad_analyzer = SileroVADAnalyzer( - sample_rate=16000, - params=VADParams( - confidence=BREEZE_BUDDY_VAD_CONFIDENCE, - start_secs=BREEZE_BUDDY_VAD_START_SECS, - stop_secs=BREEZE_BUDDY_VAD_STOP_SECS, - min_volume=BREEZE_BUDDY_VAD_MIN_VOLUME, - ), - ) # Create telephony transport self.vad_analyzer = SileroVADAnalyzer(Also ensure the telephony transport creation (lines 319-345) is properly indented inside the
elseblock.
347-354: Undefined variabletemplateused after the transport blocks.The
templatevariable is referenced at lines 348-351 and 365-368, but it's loaded inside_load_template_config()and not stored as an instance attribute. The code needs access to the template object to readconfigurations.stt_languageandconfigurations.tts_voice_name.🔎 Suggested fix
In
_load_template_config(), store the template as an instance attribute:template = await get_template_by_merchant( merchant_id=merchant_id, shop_identifier=self.lead.shop_identifier if self.lead else None, name=self.lead.template, ) + self.template = template # Store for later use if not template:Then use
self.templatein the pipeline setup section.
🧹 Nitpick comments (3)
app/api/routers/breeze_buddy/__init__.py (1)
12-12: Consider moving Daily router to the "Modern RESTful Endpoints" section.The
daily_routerimport is placed under the "Deprecated routers" comment section (line 11), and itsinclude_routercall is placed after the deprecated section (line 64). Since Daily is a new feature, it should be grouped with the modern endpoints (lines 27-55) for better organization.🔎 Suggested reorganization
Move the import near other modern routers and include it in the appropriate section:
# Modern RESTful routers from app.api.routers.breeze_buddy.analytics import router as analytics_router +from app.api.routers.breeze_buddy.daily import router as daily_router # Auth, telephony, cron, websocket from app.api.routers.breeze_buddy.auth import router as auth_router ... # Deprecated routers (backward compatibility) -from app.api.routers.breeze_buddy.daily import router as daily_router from app.api.routers.breeze_buddy.deprecated import router as deprecated_routerAnd in the include section:
# WebSocket (real-time communication) router.include_router(websocket_router, prefix="", tags=["websocket"]) +# Daily (web/mobile voice transport) +router.include_router(daily_router, prefix="", tags=["daily"]) + # ============================================================================ # Deprecated Endpoints (Backward Compatibility) # ============================================================================ ... router.include_router(deprecated_router, prefix="", tags=["deprecated"]) -router.include_router(daily_router, prefix="", tags=["daily"])Also applies to: 64-64
DAILY_IMPLEMENTATION_SUMMARY.md (1)
83-95: Add language identifiers to fenced code blocks.Static analysis flagged that code blocks at lines 83 and 89 lack language specifiers, which affects syntax highlighting and accessibility.
🔎 Suggested fix
-``` +```text Phone → Twilio/Exotel → WebSocket → /ws/{provider}/callback/{template} → Agent(telephony mode) → PipelineDaily (New):
-+text
Client → POST /connect → Creates Daily Room → Returns credentials
↓</details> </blockquote></details> <details> <summary>app/api/routers/breeze_buddy/daily.py (1)</summary><blockquote> `103-108`: **Chain the exception for better debugging.** Use `raise ... from e` to preserve the exception chain and improve stack traces for debugging. <details> <summary>🔎 Suggested fix</summary> ```diff except Exception as e: logger.error(f"Failed to create Breeze Buddy Daily session: {e}", exc_info=True) raise HTTPException( status_code=500, - detail=f"Failed to create Breeze Buddy Daily session: {str(e)}", - ) + detail=f"Failed to create Breeze Buddy Daily session: {e!s}", + ) from eNote: Using
{e!s}is the explicit conversion flag format recommended by Ruff (RUF010).
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
DAILY_IMPLEMENTATION_SUMMARY.mdapp/ai/voice/agents/breeze_buddy/agent.pyapp/api/routers/breeze_buddy/__init__.pyapp/api/routers/breeze_buddy/daily.pyapp/schemas.py
🧰 Additional context used
🧬 Code graph analysis (1)
app/schemas.py (5)
app/ai/voice/agents/automatic/types/models.py (2)
TTSProvider(8-11)VoiceName(14-17)app/schemas/breeze_buddy/core.py (9)
OutboundNumberStatus(10-15)CallProvider(18-22)LeadCallStatus(25-31)LeadCallTracker(34-56)CreateOutboundNumberRequest(59-67)OutboundNumber(70-82)CreateCallExecutionConfigRequest(85-98)UpdateCallExecutionConfigRequest(101-114)CallExecutionConfig(117-133)app/ai/voice/agents/breeze_buddy/template/context.py (1)
provider(117-119)app/schemas/automatic_voice/connection.py (2)
AutomaticVoiceTTSServiceConfig(10-14)AutomaticVoiceUserConnectRequest(17-34)app/schemas/breeze_buddy/auth.py (1)
TokenData(10-16)
🪛 markdownlint-cli2 (0.18.1)
DAILY_IMPLEMENTATION_SUMMARY.md
13-13: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
71-71: Strong style
Expected: asterisk; Actual: underscore
(MD050, strong-style)
71-71: Strong style
Expected: asterisk; Actual: underscore
(MD050, strong-style)
83-83: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
89-89: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
207-207: Bare URL used
(MD034, no-bare-urls)
284-284: Strong style
Expected: asterisk; Actual: underscore
(MD050, strong-style)
284-284: Strong style
Expected: asterisk; Actual: underscore
(MD050, strong-style)
🪛 Ruff (0.14.10)
app/api/routers/breeze_buddy/daily.py
89-89: Store a reference to the return value of asyncio.create_task
(RUF006)
96-101: Consider moving this statement to an else block
(TRY300)
103-103: Do not catch blind exception: Exception
(BLE001)
105-108: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
107-107: Use explicit conversion flag
Replace with conversion flag
(RUF010)
app/ai/voice/agents/breeze_buddy/agent.py
320-320: Unexpected indentation
(invalid-syntax)
app/schemas.py
106-106: Redefinition of unused OutboundNumberStatus from line 55
(F811)
112-112: Redefinition of unused CallProvider from line 49
(F811)
117-117: Redefinition of unused LeadCallStatus from line 52
(F811)
124-124: Redefinition of unused LeadCallTracker from line 53
(F811)
147-147: Redefinition of unused CreateOutboundNumberRequest from line 51
(F811)
154-154: Redefinition of unused OutboundNumber from line 54
(F811)
165-165: Redefinition of unused CreateCallExecutionConfigRequest from line 50
(F811)
178-178: Redefinition of unused UpdateCallExecutionConfigRequest from line 56
(F811)
191-191: Redefinition of unused CallExecutionConfig from line 48
(F811)
207-207: Redefinition of unused AutomaticVoiceTTSServiceConfig from line 12
(F811)
212-212: Redefinition of unused AutomaticVoiceUserConnectRequest from line 13
(F811)
238-238: Redefinition of unused TokenData from line 38
(F811)
🔇 Additional comments (5)
DAILY_IMPLEMENTATION_SUMMARY.md (1)
1-287: Good documentation of the Daily transport implementation.The implementation summary provides clear documentation of the architecture, code flow, and usage examples. This will be valuable for onboarding and maintenance.
app/ai/voice/agents/breeze_buddy/agent.py (3)
115-167: Good extraction of template loading logic into a reusable helper.The
_load_template_configmethod cleanly centralizes template configuration loading for both Daily and telephony modes. The dynamic building oftemplate_varsfromexpected_payload_schemais well-implemented with appropriate warning logs for missing fields.
58-73: Clean transport configuration pattern.The
transport_paramsdictionary provides a clean factory pattern for creating transport-specific configurations. This makes it easy to add new transport types in the future.
537-561: Well-documented entry point for Daily-based agents.The
bot()function provides a clear entry point for Daily transport with comprehensive docstrings explaining its purpose and usage.app/api/routers/breeze_buddy/daily.py (1)
70-72: Reconsider token expiry alignment.The user token (line 71) has no expiry set and is valid indefinitely, while the bot token (line 72) explicitly expires after 3600 seconds (1 hour). This means the bot will be disconnected after 1 hour while the user token remains active indefinitely, which may cause unexpected behavior if not handled properly. Consider aligning token expiry times or documenting the intentional difference.
Likely an incorrect or invalid review comment.
| @router.post("/connect") | ||
| async def breeze_buddy_daily_connect( | ||
| request: BreezeBuddyDailyConnectRequest, | ||
| ) -> Dict[str, Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if other breeze_buddy endpoints have authentication
rg -n "Depends|get_current_user|verify_token|api_key" app/api/routers/breeze_buddy/ --type py -B2 -A2Repository: juspay/clairvoyance
Length of output: 24597
🏁 Script executed:
cat -n app/api/routers/breeze_buddy/daily.py | head -60Repository: juspay/clairvoyance
Length of output: 2267
🏁 Script executed:
cat -n app/api/routers/breeze_buddy/daily.py | sed -n '50,100p'Repository: juspay/clairvoyance
Length of output: 2091
Add authentication to the /connect endpoint.
The endpoint creates Daily rooms, generates tokens, and spawns bot processes without any authentication mechanism (no Depends(get_current_user_with_rbac) or similar). This is inconsistent with all other breeze_buddy endpoints and exposes resource-intensive operations to unauthenticated requests, enabling potential abuse (unlimited room creation, resource exhaustion).
🤖 Prompt for AI Agents
In app/api/routers/breeze_buddy/daily.py around lines 24 to 27, the /connect
endpoint is missing authentication and should require the same RBAC-checked
current user as other breeze_buddy endpoints; add a dependency parameter to the
function signature that injects get_current_user_with_rbac (import it from the
auth module if not already imported), accept the returned user (or
user_with_rbac) argument in the handler to enforce authentication/authorization,
and ensure any downstream logic uses that authenticated context so
unauthenticated requests cannot create rooms or spawn bots.
| # 4. Start the bot in background | ||
| asyncio.create_task(bot(runner_args)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Store reference to the background task to prevent silent failures.
The asyncio.create_task(bot(runner_args)) call creates a fire-and-forget task. If the bot crashes, the exception will be silently swallowed. Additionally, as noted by static analysis (RUF006), the task reference should be stored to prevent garbage collection issues.
🔎 Suggested fix
+ # Store task reference to prevent GC and enable error logging
+ bot_task = asyncio.create_task(bot(runner_args))
+ bot_task.add_done_callback(
+ lambda t: logger.error(f"Bot task failed: {t.exception()}") if t.exception() else None
+ )
- asyncio.create_task(bot(runner_args))Alternatively, consider a more robust approach with task tracking for graceful shutdown.
🧰 Tools
🪛 Ruff (0.14.10)
89-89: Store a reference to the return value of asyncio.create_task
(RUF006)
🤖 Prompt for AI Agents
In app/api/routers/breeze_buddy/daily.py around lines 88-89, the call
asyncio.create_task(bot(runner_args)) creates a fire-and-forget task that can be
garbage-collected and will swallow exceptions; store the returned Task in a
persistent variable (e.g., module-level or on the runner object or in a tracked
tasks list) and attach a done-callback that logs/handles exceptions, optionally
give the task a name for diagnostics, and ensure it is cancelled/awaited on
shutdown so failures are observable and GC issues (RUF006) are avoided.
| from datetime import datetime, time | ||
| from enum import Enum | ||
| from typing import Any, Dict, List, Optional | ||
|
|
||
| from pydantic import BaseModel, Field | ||
|
|
||
| from app.ai.voice.agents.automatic.types.models import TTSProvider, VoiceName | ||
|
|
||
|
|
||
| class OutboundNumberStatus(str, Enum): | ||
| AVAILABLE = "AVAILABLE" | ||
| IN_USE = "IN_USE" | ||
| DISABLED = "DISABLED" | ||
|
|
||
|
|
||
| class CallProvider(str, Enum): | ||
| TWILIO = "TWILIO" | ||
| EXOTEL = "EXOTEL" | ||
|
|
||
|
|
||
| class LeadCallStatus(str, Enum): | ||
| BACKLOG = "BACKLOG" | ||
| PROCESSING = "PROCESSING" | ||
| FINISHED = "FINISHED" | ||
| RETRY = "RETRY" | ||
|
|
||
|
|
||
| class LeadCallTracker(BaseModel): | ||
| id: str | ||
| outbound_number_id: Optional[str] = None | ||
| merchant_id: str | ||
| template: str | ||
| shop_identifier: Optional[str] = None | ||
| request_id: Optional[str] = None | ||
| attempt_count: int = 0 | ||
| next_attempt_at: Optional[datetime] = None | ||
| payload: Optional[Dict[str, Any]] = None | ||
| metaData: Optional[Dict[str, Any]] = None | ||
| recording_url: Optional[str] = None | ||
| status: LeadCallStatus = LeadCallStatus.BACKLOG | ||
| outcome: Optional[str] = None | ||
| call_id: Optional[str] = None | ||
| call_initiated_time: Optional[datetime] = None | ||
| call_end_time: Optional[datetime] = None | ||
| cost: Optional[float] = None | ||
| is_locked: bool = False | ||
| created_at: Optional[datetime] = None | ||
| updated_at: Optional[datetime] = None | ||
|
|
||
|
|
||
| class CreateOutboundNumberRequest(BaseModel): | ||
| number: str | ||
| provider: CallProvider | ||
| status: OutboundNumberStatus = OutboundNumberStatus.AVAILABLE | ||
| maximum_channels: Optional[int] = None | ||
|
|
||
|
|
||
| class OutboundNumber(BaseModel): | ||
| id: str | ||
| number: str | ||
| provider: CallProvider | ||
| status: OutboundNumberStatus | ||
| channels: Optional[int] = None | ||
| maximum_channels: Optional[int] = None | ||
| created_at: Optional[datetime] = None | ||
| updated_at: Optional[datetime] = None | ||
|
|
||
|
|
||
| class CreateCallExecutionConfigRequest(BaseModel): | ||
| initial_offset: int | ||
| retry_offset: int | ||
| call_start_time: time | ||
| call_end_time: time | ||
| max_retry: int | ||
| calling_provider: CallProvider | ||
| merchant_id: str | ||
| workflow: str | ||
| shop_identifier: Optional[str] = None | ||
| enable_international_call: bool = True | ||
|
|
||
|
|
||
| class UpdateCallExecutionConfigRequest(BaseModel): | ||
| merchant_id: str | ||
| workflow: str | ||
| shop_identifier: Optional[str] = None | ||
| initial_offset: Optional[int] = None | ||
| retry_offset: Optional[int] = None | ||
| call_start_time: Optional[time] = None | ||
| call_end_time: Optional[time] = None | ||
| max_retry: Optional[int] = None | ||
| calling_provider: Optional[CallProvider] = None | ||
| enable_international_call: Optional[bool] = None | ||
|
|
||
|
|
||
| class CallExecutionConfig(BaseModel): | ||
| id: str | ||
| initial_offset: int | ||
| retry_offset: int | ||
| call_start_time: time | ||
| call_end_time: time | ||
| max_retry: int | ||
| calling_provider: CallProvider | ||
| merchant_id: str | ||
| template: str | ||
| shop_identifier: Optional[str] = None | ||
| enable_international_call: bool = True | ||
| created_at: Optional[datetime] = None | ||
| updated_at: Optional[datetime] = None | ||
|
|
||
|
|
||
| class AutomaticVoiceTTSServiceConfig(BaseModel): | ||
| ttsProvider: TTSProvider | ||
| voiceName: VoiceName | ||
|
|
||
|
|
||
| class AutomaticVoiceUserConnectRequest(BaseModel): | ||
| sessionId: Optional[str] = None | ||
| mode: Optional[str] = None | ||
| eulerToken: Optional[str] = None | ||
| breezeToken: Optional[str] = None | ||
| shopUrl: Optional[str] = None | ||
| shopId: Optional[str] = None | ||
| shopType: Optional[str] = None | ||
| userName: Optional[str] = None | ||
| email: Optional[str] = None | ||
| ttsService: Optional[AutomaticVoiceTTSServiceConfig] = None | ||
| merchantId: Optional[str] = None | ||
| platformIntegrations: Optional[List[str]] = None | ||
| resellerId: Optional[str] = None | ||
| customerId: Optional[str] = None | ||
| shopifyConnectedShop: Optional[str] = None | ||
|
|
||
|
|
||
| class BreezeBuddyDailyConnectRequest(BaseModel): | ||
| """Request model for Breeze Buddy Daily transport connection.""" | ||
|
|
||
| call_sid: Optional[str] = Field( | ||
| None, description="Unique identifier for the call/session" | ||
| ) | ||
|
|
||
|
|
||
| class TokenData(BaseModel): | ||
| """Token data model for JWT payload""" | ||
|
|
||
| user_id: Optional[str] = None | ||
| username: Optional[str] = None | ||
| email: Optional[str] = None | ||
| scopes: list[str] = Field(default_factory=list) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Duplicate model definitions shadow existing imports.
Lines 97-244 redefine models (OutboundNumberStatus, CallProvider, LeadCallTracker, etc.) that are already imported from the organized submodules at lines 11-57. This directly contradicts the file's deprecation notice (lines 1-9) which states to use the organized schema structure.
Static analysis correctly flags these as F811 (redefinition) errors. These duplicate definitions will shadow the imported ones, potentially causing confusion and inconsistency.
The only genuinely new model is BreezeBuddyDailyConnectRequest. It should be added to the proper submodule instead.
🔎 Suggested fix
- Remove all duplicate definitions (lines 97-244)
- Create the new
BreezeBuddyDailyConnectRequestin the proper location:
Create or update app/schemas/breeze_buddy/daily.py:
from typing import Optional
from pydantic import BaseModel, Field
class BreezeBuddyDailyConnectRequest(BaseModel):
"""Request model for Breeze Buddy Daily transport connection."""
call_sid: Optional[str] = Field(
None, description="Unique identifier for the call/session"
)- Update
app/schemas.pyto import and re-export only the new model:
from app.schemas.breeze_buddy.core import (
CallExecutionConfig,
...
)
+from app.schemas.breeze_buddy.daily import BreezeBuddyDailyConnectRequest
__all__ = [
...
+ "BreezeBuddyDailyConnectRequest",
]
-from datetime import datetime, time
-from enum import Enum
-... (remove all lines 97-244)Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Ruff (0.14.10)
106-106: Redefinition of unused OutboundNumberStatus from line 55
(F811)
112-112: Redefinition of unused CallProvider from line 49
(F811)
117-117: Redefinition of unused LeadCallStatus from line 52
(F811)
124-124: Redefinition of unused LeadCallTracker from line 53
(F811)
147-147: Redefinition of unused CreateOutboundNumberRequest from line 51
(F811)
154-154: Redefinition of unused OutboundNumber from line 54
(F811)
165-165: Redefinition of unused CreateCallExecutionConfigRequest from line 50
(F811)
178-178: Redefinition of unused UpdateCallExecutionConfigRequest from line 56
(F811)
191-191: Redefinition of unused CallExecutionConfig from line 48
(F811)
207-207: Redefinition of unused AutomaticVoiceTTSServiceConfig from line 12
(F811)
212-212: Redefinition of unused AutomaticVoiceUserConnectRequest from line 13
(F811)
238-238: Redefinition of unused TokenData from line 38
(F811)
🤖 Prompt for AI Agents
In app/schemas.py around lines 97 to 244 you have duplicate model and enum
definitions that shadow imports from organized submodules; remove all duplicated
definitions in that range (keep only imports/re-exports in this file), extract
the single new model BreezeBuddyDailyConnectRequest into a new module
app/schemas/breeze_buddy/daily.py (create file and define the Pydantic model
there), then update app/schemas.py to import and re-export
BreezeBuddyDailyConnectRequest from app.schemas.breeze_buddy.daily (and update
any __all__ or export lists accordingly) so the central schema file only
re-exports models instead of redefining them.
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.