fix: fix circuit breaker failure recording

fix: fix circuit breaker failure recording
chore: provide advanced configuration option in UI for campaigns
This commit is contained in:
Abhishek Kumar 2026-03-05 13:43:13 +05:30
parent 628132f29b
commit 3ea235a666
17 changed files with 448 additions and 58 deletions

View file

@ -9,6 +9,7 @@ from api.constants import DEFAULT_ORG_CONCURRENCY_LIMIT
from api.db import db_client
from api.db.models import QueuedRunModel, WorkflowRunModel
from api.enums import OrganizationConfigurationKey, WorkflowRunState
from api.services.campaign.circuit_breaker import circuit_breaker
from api.services.campaign.errors import (
ConcurrentSlotAcquisitionError,
PhoneNumberPoolExhaustedError,
@ -315,6 +316,9 @@ class CampaignCallDispatcher:
},
)
# Record call initiation failure in circuit breaker
await circuit_breaker.record_and_evaluate(campaign.id, is_failure=True)
# Release concurrent slot on failure
mapping = await rate_limiter.get_workflow_slot_mapping(workflow_run.id)
if mapping:

View file

@ -14,7 +14,6 @@ setup_logging()
import asyncio
import json
import signal
import time
from typing import Dict, Optional, Set
from urllib.parse import urlparse
@ -628,7 +627,7 @@ class ARIConnection:
bridge_id = ctx.get("bridge_id")
transfer_state = ctx.get("transfer_state")
# Check if this is a call transfer scenario external channel. Skip full teardown if
# Check if this is a call transfer scenario external channel. Skip full teardown if
# transfer is in progress and this is the external media channel
# During call transfer, we preserve the caller-destination bridge
if (

View file

@ -45,13 +45,16 @@ class ARIBridgeSwapStrategy(TransferStrategy):
from api.services.telephony.call_transfer_manager import (
get_call_transfer_manager,
)
auth = BasicAuth(app_name, app_password)
# Get call transfer manager instance
call_transfer_manager = await get_call_transfer_manager()
# 1. Find active transfer context for this caller channel
transfer_context = await call_transfer_manager.find_transfer_context_for_call(channel_id)
transfer_context = (
await call_transfer_manager.find_transfer_context_for_call(channel_id)
)
if not transfer_context:
logger.error(
f"[ARI Transfer] No active transfer context found for caller {channel_id}"
@ -178,6 +181,7 @@ class ARIBridgeSwapStrategy(TransferStrategy):
logger.exception(f"Failed to execute ARI transfer: {e}")
return False
class ARIHangupStrategy(HangupStrategy):
"""Implements hangup for Asterisk ARI channels."""
@ -223,4 +227,4 @@ class ARIHangupStrategy(HangupStrategy):
except Exception as e:
logger.exception(f"Failed to hang up Asterisk channel: {e}")
return False
return False

View file

@ -455,7 +455,9 @@ class ARIProvider(TelephonyProvider):
}
except Exception as e:
logger.error(f"[ARI Transfer] Failed to originate call transfer destination channel: {e}")
logger.error(
f"[ARI Transfer] Failed to originate call transfer destination channel: {e}"
)
await call_transfer_manager.remove_transfer_context(transfer_id)
raise

View file

@ -107,9 +107,10 @@ class CloudonixProvider(TelephonyProvider):
}
data["machineDetection"] = "DetectMessageEnd"
data["asyncAmd"] = True
data["asyncAmdStatusCallback"] = f"{backend_endpoint}/api/v1/telephony/cloudonix/amd-callback/{workflow_run_id}"
data["asyncAmdStatusCallbackMethod"]= "POST"
data["asyncAmdStatusCallback"] = (
f"{backend_endpoint}/api/v1/telephony/cloudonix/amd-callback/{workflow_run_id}"
)
data["asyncAmdStatusCallbackMethod"] = "POST"
# TODO: Cloudonix status callbacks are spammy, so commenting it out. Can send it to
# some persistent logging system instead of transcational database.

View file

@ -76,20 +76,26 @@ class TwilioConferenceStrategy(TransferStrategy):
)
# 3. Clean up transfer context after successful transfer
await self._cleanup_transfer_context(transfer_context.transfer_id)
await self._cleanup_transfer_context(
transfer_context.transfer_id
)
return True
elif response.status == 404:
logger.error(
f"Failed to transfer Twilio call {call_sid}: Call not found (404)"
)
await self._cleanup_transfer_context(transfer_context.transfer_id)
await self._cleanup_transfer_context(
transfer_context.transfer_id
)
return False
else:
logger.error(
f"Failed to transfer Twilio call {call_sid} to conference {conference_name}: "
f"Status {response.status}, Response: {response_text}"
)
await self._cleanup_transfer_context(transfer_context.transfer_id)
await self._cleanup_transfer_context(
transfer_context.transfer_id
)
return False
except Exception as e:
@ -132,7 +138,7 @@ class TwilioConferenceStrategy(TransferStrategy):
from api.services.telephony.call_transfer_manager import (
get_call_transfer_manager,
)
call_transfer_manager = await get_call_transfer_manager()
await call_transfer_manager.remove_transfer_context(transfer_id)
except Exception as e:
@ -183,4 +189,4 @@ class TwilioHangupStrategy(HangupStrategy):
except Exception as e:
logger.exception(f"Failed to hang up Twilio call: {e}")
return False
return False