webagents 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- webagents/__init__.py +18 -0
- webagents/__main__.py +55 -0
- webagents/agents/__init__.py +13 -0
- webagents/agents/core/__init__.py +19 -0
- webagents/agents/core/base_agent.py +1834 -0
- webagents/agents/core/handoffs.py +293 -0
- webagents/agents/handoffs/__init__.py +0 -0
- webagents/agents/interfaces/__init__.py +0 -0
- webagents/agents/lifecycle/__init__.py +0 -0
- webagents/agents/skills/__init__.py +109 -0
- webagents/agents/skills/base.py +136 -0
- webagents/agents/skills/core/__init__.py +8 -0
- webagents/agents/skills/core/guardrails/__init__.py +0 -0
- webagents/agents/skills/core/llm/__init__.py +0 -0
- webagents/agents/skills/core/llm/anthropic/__init__.py +1 -0
- webagents/agents/skills/core/llm/litellm/__init__.py +10 -0
- webagents/agents/skills/core/llm/litellm/skill.py +538 -0
- webagents/agents/skills/core/llm/openai/__init__.py +1 -0
- webagents/agents/skills/core/llm/xai/__init__.py +1 -0
- webagents/agents/skills/core/mcp/README.md +375 -0
- webagents/agents/skills/core/mcp/__init__.py +15 -0
- webagents/agents/skills/core/mcp/skill.py +731 -0
- webagents/agents/skills/core/memory/__init__.py +11 -0
- webagents/agents/skills/core/memory/long_term_memory/__init__.py +10 -0
- webagents/agents/skills/core/memory/long_term_memory/memory_skill.py +639 -0
- webagents/agents/skills/core/memory/short_term_memory/__init__.py +9 -0
- webagents/agents/skills/core/memory/short_term_memory/skill.py +341 -0
- webagents/agents/skills/core/memory/vector_memory/skill.py +447 -0
- webagents/agents/skills/core/planning/__init__.py +9 -0
- webagents/agents/skills/core/planning/planner.py +343 -0
- webagents/agents/skills/ecosystem/__init__.py +0 -0
- webagents/agents/skills/ecosystem/crewai/__init__.py +1 -0
- webagents/agents/skills/ecosystem/database/__init__.py +1 -0
- webagents/agents/skills/ecosystem/filesystem/__init__.py +0 -0
- webagents/agents/skills/ecosystem/google/__init__.py +0 -0
- webagents/agents/skills/ecosystem/google/calendar/__init__.py +6 -0
- webagents/agents/skills/ecosystem/google/calendar/skill.py +306 -0
- webagents/agents/skills/ecosystem/n8n/__init__.py +0 -0
- webagents/agents/skills/ecosystem/openai_agents/__init__.py +0 -0
- webagents/agents/skills/ecosystem/web/__init__.py +0 -0
- webagents/agents/skills/ecosystem/zapier/__init__.py +0 -0
- webagents/agents/skills/robutler/__init__.py +11 -0
- webagents/agents/skills/robutler/auth/README.md +63 -0
- webagents/agents/skills/robutler/auth/__init__.py +17 -0
- webagents/agents/skills/robutler/auth/skill.py +354 -0
- webagents/agents/skills/robutler/crm/__init__.py +18 -0
- webagents/agents/skills/robutler/crm/skill.py +368 -0
- webagents/agents/skills/robutler/discovery/README.md +281 -0
- webagents/agents/skills/robutler/discovery/__init__.py +16 -0
- webagents/agents/skills/robutler/discovery/skill.py +230 -0
- webagents/agents/skills/robutler/kv/__init__.py +6 -0
- webagents/agents/skills/robutler/kv/skill.py +80 -0
- webagents/agents/skills/robutler/message_history/__init__.py +9 -0
- webagents/agents/skills/robutler/message_history/skill.py +270 -0
- webagents/agents/skills/robutler/messages/__init__.py +0 -0
- webagents/agents/skills/robutler/nli/__init__.py +13 -0
- webagents/agents/skills/robutler/nli/skill.py +687 -0
- webagents/agents/skills/robutler/notifications/__init__.py +5 -0
- webagents/agents/skills/robutler/notifications/skill.py +141 -0
- webagents/agents/skills/robutler/payments/__init__.py +41 -0
- webagents/agents/skills/robutler/payments/exceptions.py +255 -0
- webagents/agents/skills/robutler/payments/skill.py +610 -0
- webagents/agents/skills/robutler/storage/__init__.py +10 -0
- webagents/agents/skills/robutler/storage/files/__init__.py +9 -0
- webagents/agents/skills/robutler/storage/files/skill.py +445 -0
- webagents/agents/skills/robutler/storage/json/__init__.py +9 -0
- webagents/agents/skills/robutler/storage/json/skill.py +336 -0
- webagents/agents/skills/robutler/storage/kv/skill.py +88 -0
- webagents/agents/skills/robutler/storage.py +389 -0
- webagents/agents/tools/__init__.py +0 -0
- webagents/agents/tools/decorators.py +426 -0
- webagents/agents/tracing/__init__.py +0 -0
- webagents/agents/workflows/__init__.py +0 -0
- webagents/scripts/__init__.py +0 -0
- webagents/server/__init__.py +28 -0
- webagents/server/context/__init__.py +0 -0
- webagents/server/context/context_vars.py +121 -0
- webagents/server/core/__init__.py +0 -0
- webagents/server/core/app.py +843 -0
- webagents/server/core/middleware.py +69 -0
- webagents/server/core/models.py +98 -0
- webagents/server/core/monitoring.py +59 -0
- webagents/server/endpoints/__init__.py +0 -0
- webagents/server/interfaces/__init__.py +0 -0
- webagents/server/middleware.py +330 -0
- webagents/server/models.py +92 -0
- webagents/server/monitoring.py +659 -0
- webagents/utils/__init__.py +0 -0
- webagents/utils/logging.py +359 -0
- webagents-0.1.0.dist-info/METADATA +230 -0
- webagents-0.1.0.dist-info/RECORD +94 -0
- webagents-0.1.0.dist-info/WHEEL +4 -0
- webagents-0.1.0.dist-info/entry_points.txt +2 -0
- webagents-0.1.0.dist-info/licenses/LICENSE +20 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
import os
|
2
|
+
from typing import Any, Dict, Optional, List
|
3
|
+
|
4
|
+
import httpx
|
5
|
+
|
6
|
+
from webagents.agents.skills.base import Skill
|
7
|
+
from webagents.agents.tools.decorators import tool, prompt
|
8
|
+
from webagents.server.context.context_vars import get_context
|
9
|
+
from webagents.utils.logging import get_logger, log_skill_event, log_tool_execution
|
10
|
+
|
11
|
+
|
12
|
+
def _resolve_portal_url() -> str:
|
13
|
+
return os.getenv("ROBUTLER_INTERNAL_API_URL") or os.getenv("ROBUTLER_API_URL", "http://localhost:3000")
|
14
|
+
|
15
|
+
|
16
|
+
class NotificationsSkill(Skill):
|
17
|
+
"""Send push notifications to the owner of this agent (owner-only).
|
18
|
+
|
19
|
+
This skill exposes a single owner-scoped tool that sends a push notification via the
|
20
|
+
portal notifications API (POST /api/notifications/send). The notification will be
|
21
|
+
targeted to this agent's owner user ID.
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None) -> None:
|
25
|
+
# Align with other skills (e.g., VectorMemorySkill) for consistent SDK behavior
|
26
|
+
super().__init__(config or {}, scope="all")
|
27
|
+
self.config = config or {}
|
28
|
+
self.logger = None
|
29
|
+
|
30
|
+
async def initialize(self, agent) -> None:
|
31
|
+
self.agent = agent
|
32
|
+
self.logger = get_logger('skill.notifications', agent.name)
|
33
|
+
# Log level is configured globally via setup_logging()
|
34
|
+
log_skill_event(agent.name, 'notifications', 'initialized', {})
|
35
|
+
# Cache agent API key for later use
|
36
|
+
try:
|
37
|
+
self.agent_api_key: Optional[str] = getattr(agent, 'api_key', None)
|
38
|
+
except Exception:
|
39
|
+
self.agent_api_key = None
|
40
|
+
|
41
|
+
@prompt(priority=60, scope=["owner"]) # Owner sees how to use it
|
42
|
+
def get_notifications_prompt(self) -> str:
|
43
|
+
return (
|
44
|
+
"You can use send_notification(title, body, tag?, type?, priority?, requireInteraction?, silent?, ttl?) to send a push notifications to the owner of this agent. Use it only when explicitly asked to do so."
|
45
|
+
)
|
46
|
+
|
47
|
+
def _get_owner_user_id(self) -> Optional[str]:
|
48
|
+
# Prefer agent metadata set by factory
|
49
|
+
try:
|
50
|
+
owner_id = getattr(self.agent, 'owner_user_id', None)
|
51
|
+
if owner_id:
|
52
|
+
return owner_id
|
53
|
+
except Exception:
|
54
|
+
pass
|
55
|
+
# Fallback to auth context if available
|
56
|
+
try:
|
57
|
+
ctx = get_context()
|
58
|
+
auth_ctx = getattr(ctx, 'auth', None) or (ctx and ctx.get('auth'))
|
59
|
+
if auth_ctx and getattr(auth_ctx, 'scope', '').upper() == 'OWNER':
|
60
|
+
# Not strictly the owner ID, but if missing we skip
|
61
|
+
return getattr(auth_ctx, 'user_id', None)
|
62
|
+
except Exception:
|
63
|
+
pass
|
64
|
+
return None
|
65
|
+
|
66
|
+
async def _post_notification(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
67
|
+
base_url = _resolve_portal_url()
|
68
|
+
url = f"{base_url}/api/notifications/send"
|
69
|
+
headers = {
|
70
|
+
'Content-Type': 'application/json'
|
71
|
+
}
|
72
|
+
# Use agent API key only (no service tokens here)
|
73
|
+
api_key = getattr(self, 'agent_api_key', None) or getattr(self.agent, 'api_key', None)
|
74
|
+
if not api_key:
|
75
|
+
raise RuntimeError("Agent API key unavailable for notifications")
|
76
|
+
headers['X-API-Key'] = api_key
|
77
|
+
|
78
|
+
async with httpx.AsyncClient(timeout=20) as client:
|
79
|
+
resp = await client.post(url, json=payload, headers=headers)
|
80
|
+
try:
|
81
|
+
data = resp.json()
|
82
|
+
except Exception:
|
83
|
+
data = {'status': resp.status_code, 'text': resp.text}
|
84
|
+
if resp.status_code >= 400:
|
85
|
+
raise RuntimeError(f"Notification send failed: {resp.status_code} {data}")
|
86
|
+
return data
|
87
|
+
|
88
|
+
@tool(
|
89
|
+
name="send_notification",
|
90
|
+
description=(
|
91
|
+
"Send a push notification. "
|
92
|
+
"Parameters: title (string), body (string). Optional: tag (string), type (chat_message|agent_update|system_announcement|marketing), "
|
93
|
+
"priority (low|normal|high|urgent), requireInteraction (bool), silent (bool), ttl (seconds)."
|
94
|
+
),
|
95
|
+
scope="owner",
|
96
|
+
)
|
97
|
+
async def send_notification(
|
98
|
+
self,
|
99
|
+
title: str,
|
100
|
+
body: str,
|
101
|
+
tag: Optional[str] = None,
|
102
|
+
type: Optional[str] = "agent_update",
|
103
|
+
priority: Optional[str] = "normal",
|
104
|
+
requireInteraction: Optional[bool] = False,
|
105
|
+
silent: Optional[bool] = False,
|
106
|
+
ttl: Optional[int] = 86400,
|
107
|
+
context: Any = None,
|
108
|
+
) -> str:
|
109
|
+
owner_id = self._get_owner_user_id()
|
110
|
+
if not owner_id:
|
111
|
+
return "❌ Cannot resolve agent owner user ID"
|
112
|
+
|
113
|
+
payload: Dict[str, Any] = {
|
114
|
+
'title': title,
|
115
|
+
'body': body,
|
116
|
+
'type': type or 'agent_update',
|
117
|
+
'priority': priority or 'normal',
|
118
|
+
'userIds': [owner_id],
|
119
|
+
'requireInteraction': bool(requireInteraction),
|
120
|
+
'silent': bool(silent),
|
121
|
+
'ttl': int(ttl or 86400),
|
122
|
+
}
|
123
|
+
if tag:
|
124
|
+
payload['tag'] = tag
|
125
|
+
|
126
|
+
# Call API
|
127
|
+
try:
|
128
|
+
result = await self._post_notification(payload)
|
129
|
+
log_tool_execution(self.agent.name, 'notifications.send_notification', 'success', {
|
130
|
+
'owner_id': owner_id,
|
131
|
+
'title': title[:50]
|
132
|
+
})
|
133
|
+
return f"✅ Notification queued: {result.get('message', 'ok')}"
|
134
|
+
except Exception as e:
|
135
|
+
log_tool_execution(self.agent.name, 'notifications.send_notification', 'failure', {
|
136
|
+
'owner_id': owner_id,
|
137
|
+
'error': str(e)
|
138
|
+
})
|
139
|
+
return f"❌ Failed to send notification: {e}"
|
140
|
+
|
141
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
"""
|
2
|
+
PaymentSkill Package - WebAgents V2.0
|
3
|
+
|
4
|
+
Payment processing and billing skill for WebAgents platform.
|
5
|
+
Validates payment tokens, calculates costs using LiteLLM, and charges on connection finalization.
|
6
|
+
Based on webagents_v1 implementation patterns.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from .skill import PaymentSkill, PaymentContext, PricingInfo, pricing
|
10
|
+
from .exceptions import (
|
11
|
+
PaymentError,
|
12
|
+
PaymentTokenRequiredError,
|
13
|
+
PaymentTokenInvalidError,
|
14
|
+
InsufficientBalanceError,
|
15
|
+
PaymentChargingError,
|
16
|
+
PaymentPlatformUnavailableError,
|
17
|
+
PaymentConfigurationError,
|
18
|
+
# Legacy compatibility
|
19
|
+
PaymentValidationError,
|
20
|
+
PaymentRequiredError
|
21
|
+
)
|
22
|
+
|
23
|
+
__all__ = [
|
24
|
+
# Main classes
|
25
|
+
"PaymentSkill",
|
26
|
+
"PaymentContext",
|
27
|
+
# Pricing decorators
|
28
|
+
"PricingInfo",
|
29
|
+
"pricing",
|
30
|
+
# New comprehensive error hierarchy
|
31
|
+
"PaymentError",
|
32
|
+
"PaymentTokenRequiredError",
|
33
|
+
"PaymentTokenInvalidError",
|
34
|
+
"InsufficientBalanceError",
|
35
|
+
"PaymentChargingError",
|
36
|
+
"PaymentPlatformUnavailableError",
|
37
|
+
"PaymentConfigurationError",
|
38
|
+
# Legacy compatibility
|
39
|
+
"PaymentValidationError",
|
40
|
+
"PaymentRequiredError"
|
41
|
+
]
|
@@ -0,0 +1,255 @@
|
|
1
|
+
"""
|
2
|
+
Payment Exceptions - WebAgents V2.0 Platform Integration
|
3
|
+
|
4
|
+
Comprehensive payment error hierarchy for distinguishing between different 402 payment failure scenarios.
|
5
|
+
Provides specific error codes, subcodes, and context for better error handling and user experience.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, Any, Optional
|
9
|
+
from decimal import Decimal
|
10
|
+
|
11
|
+
|
12
|
+
class PaymentError(Exception):
|
13
|
+
"""Base payment error with 402 status code and detailed context"""
|
14
|
+
|
15
|
+
def __init__(self,
|
16
|
+
message: str,
|
17
|
+
error_code: str,
|
18
|
+
subcode: Optional[str] = None,
|
19
|
+
context: Optional[Dict[str, Any]] = None,
|
20
|
+
user_message: Optional[str] = None):
|
21
|
+
super().__init__(message)
|
22
|
+
self.status_code = 402 # Payment Required
|
23
|
+
self.error_code = error_code
|
24
|
+
self.subcode = subcode
|
25
|
+
self.context = context or {}
|
26
|
+
self.user_message = user_message or message
|
27
|
+
|
28
|
+
def to_dict(self) -> Dict[str, Any]:
|
29
|
+
"""Convert error to dictionary for API responses"""
|
30
|
+
return {
|
31
|
+
'error': self.error_code,
|
32
|
+
'subcode': self.subcode,
|
33
|
+
'message': str(self),
|
34
|
+
'user_message': self.user_message,
|
35
|
+
'status_code': self.status_code,
|
36
|
+
'context': self.context
|
37
|
+
}
|
38
|
+
|
39
|
+
def __str__(self) -> str:
|
40
|
+
parts = [self.error_code]
|
41
|
+
if self.subcode:
|
42
|
+
parts.append(f"({self.subcode})")
|
43
|
+
parts.append(f": {super().__str__()}")
|
44
|
+
return " ".join(parts)
|
45
|
+
|
46
|
+
# FastAPI/Starlette HTTPException expects a `detail` field for structured errors.
|
47
|
+
# Our server checks for `hasattr(e, 'status_code') and hasattr(e, 'detail')` to
|
48
|
+
# map domain errors to HTTP responses. Expose detail dynamically from to_dict().
|
49
|
+
@property
|
50
|
+
def detail(self) -> Dict[str, Any]:
|
51
|
+
return self.to_dict()
|
52
|
+
|
53
|
+
|
54
|
+
class PaymentTokenRequiredError(PaymentError):
|
55
|
+
"""Raised when payment token is required but not provided"""
|
56
|
+
|
57
|
+
def __init__(self, agent_name: Optional[str] = None):
|
58
|
+
context = {}
|
59
|
+
if agent_name:
|
60
|
+
context['agent_name'] = agent_name
|
61
|
+
|
62
|
+
super().__init__(
|
63
|
+
message="Payment token required for billing-enabled agent",
|
64
|
+
error_code="PAYMENT_TOKEN_REQUIRED",
|
65
|
+
context=context,
|
66
|
+
user_message="This agent requires payment. Please provide a valid payment token."
|
67
|
+
)
|
68
|
+
|
69
|
+
|
70
|
+
class PaymentTokenInvalidError(PaymentError):
|
71
|
+
"""Raised when payment token is invalid or expired"""
|
72
|
+
|
73
|
+
def __init__(self,
|
74
|
+
token_prefix: Optional[str] = None,
|
75
|
+
reason: Optional[str] = None):
|
76
|
+
context = {}
|
77
|
+
if token_prefix:
|
78
|
+
context['token_prefix'] = token_prefix
|
79
|
+
if reason:
|
80
|
+
context['validation_error'] = reason
|
81
|
+
|
82
|
+
subcode = None
|
83
|
+
user_message = "Payment token is invalid or expired. Please check your token and try again."
|
84
|
+
|
85
|
+
if reason:
|
86
|
+
if "expired" in reason.lower():
|
87
|
+
subcode = "TOKEN_EXPIRED"
|
88
|
+
user_message = "Payment token has expired. Please obtain a new token."
|
89
|
+
elif "not found" in reason.lower():
|
90
|
+
subcode = "TOKEN_NOT_FOUND"
|
91
|
+
user_message = "Payment token not found. Please check your token and try again."
|
92
|
+
elif "malformed" in reason.lower():
|
93
|
+
subcode = "TOKEN_MALFORMED"
|
94
|
+
user_message = "Payment token format is invalid. Please check your token."
|
95
|
+
|
96
|
+
super().__init__(
|
97
|
+
message=f"Payment token validation failed: {reason}" if reason else "Payment token is invalid",
|
98
|
+
error_code="PAYMENT_TOKEN_INVALID",
|
99
|
+
subcode=subcode,
|
100
|
+
context=context,
|
101
|
+
user_message=user_message
|
102
|
+
)
|
103
|
+
|
104
|
+
|
105
|
+
class InsufficientBalanceError(PaymentError):
|
106
|
+
"""Raised when payment token balance is insufficient"""
|
107
|
+
|
108
|
+
def __init__(self,
|
109
|
+
current_balance: float,
|
110
|
+
required_balance: float,
|
111
|
+
token_prefix: Optional[str] = None):
|
112
|
+
context = {
|
113
|
+
'current_balance': current_balance,
|
114
|
+
'required_balance': required_balance,
|
115
|
+
'shortfall': required_balance - current_balance
|
116
|
+
}
|
117
|
+
if token_prefix:
|
118
|
+
context['token_prefix'] = token_prefix
|
119
|
+
|
120
|
+
super().__init__(
|
121
|
+
message=f"Insufficient balance: ${current_balance:.2f} < ${required_balance:.2f} required",
|
122
|
+
error_code="INSUFFICIENT_BALANCE",
|
123
|
+
context=context,
|
124
|
+
user_message=f"Insufficient credits. You have ${current_balance:.2f} but need ${required_balance:.2f}. Please add more credits to your account."
|
125
|
+
)
|
126
|
+
|
127
|
+
|
128
|
+
class PaymentChargingError(PaymentError):
|
129
|
+
"""Raised when charging/redeeming payment token fails"""
|
130
|
+
|
131
|
+
def __init__(self,
|
132
|
+
amount: float,
|
133
|
+
token_prefix: Optional[str] = None,
|
134
|
+
reason: Optional[str] = None):
|
135
|
+
context = {
|
136
|
+
'charge_amount': amount
|
137
|
+
}
|
138
|
+
if token_prefix:
|
139
|
+
context['token_prefix'] = token_prefix
|
140
|
+
if reason:
|
141
|
+
context['charge_error'] = reason
|
142
|
+
|
143
|
+
subcode = None
|
144
|
+
user_message = "Payment processing failed. Please try again or contact support."
|
145
|
+
|
146
|
+
if reason:
|
147
|
+
if "insufficient" in reason.lower():
|
148
|
+
subcode = "INSUFFICIENT_FUNDS"
|
149
|
+
user_message = "Insufficient funds for this transaction. Please add more credits."
|
150
|
+
elif "expired" in reason.lower():
|
151
|
+
subcode = "TOKEN_EXPIRED_DURING_CHARGE"
|
152
|
+
user_message = "Payment token expired during transaction. Please obtain a new token."
|
153
|
+
elif "limit" in reason.lower():
|
154
|
+
subcode = "SPENDING_LIMIT_EXCEEDED"
|
155
|
+
user_message = "Spending limit exceeded. Please check your account limits."
|
156
|
+
|
157
|
+
super().__init__(
|
158
|
+
message=f"Payment charging failed: {reason}" if reason else f"Failed to charge ${amount:.2f}",
|
159
|
+
error_code="PAYMENT_CHARGING_FAILED",
|
160
|
+
subcode=subcode,
|
161
|
+
context=context,
|
162
|
+
user_message=user_message
|
163
|
+
)
|
164
|
+
|
165
|
+
|
166
|
+
class PaymentPlatformUnavailableError(PaymentError):
|
167
|
+
"""Raised when payment platform is unavailable"""
|
168
|
+
|
169
|
+
def __init__(self, operation: Optional[str] = None):
|
170
|
+
context = {}
|
171
|
+
if operation:
|
172
|
+
context['attempted_operation'] = operation
|
173
|
+
|
174
|
+
super().__init__(
|
175
|
+
message=f"Payment platform unavailable for {operation}" if operation else "Payment platform unavailable",
|
176
|
+
error_code="PAYMENT_PLATFORM_UNAVAILABLE",
|
177
|
+
context=context,
|
178
|
+
user_message="Payment system is temporarily unavailable. Please try again later."
|
179
|
+
)
|
180
|
+
|
181
|
+
|
182
|
+
class PaymentConfigurationError(PaymentError):
|
183
|
+
"""Raised when payment configuration is invalid"""
|
184
|
+
|
185
|
+
def __init__(self,
|
186
|
+
config_issue: str,
|
187
|
+
details: Optional[str] = None):
|
188
|
+
context = {
|
189
|
+
'config_issue': config_issue
|
190
|
+
}
|
191
|
+
if details:
|
192
|
+
context['details'] = details
|
193
|
+
|
194
|
+
super().__init__(
|
195
|
+
message=f"Payment configuration error: {config_issue}",
|
196
|
+
error_code="PAYMENT_CONFIG_ERROR",
|
197
|
+
context=context,
|
198
|
+
user_message="Payment system configuration error. Please contact support."
|
199
|
+
)
|
200
|
+
|
201
|
+
|
202
|
+
# Convenience functions for common error scenarios
|
203
|
+
def create_token_required_error(agent_name: Optional[str] = None) -> PaymentTokenRequiredError:
|
204
|
+
"""Create a payment token required error"""
|
205
|
+
return PaymentTokenRequiredError(agent_name=agent_name)
|
206
|
+
|
207
|
+
|
208
|
+
def create_token_invalid_error(token_prefix: Optional[str] = None,
|
209
|
+
reason: Optional[str] = None) -> PaymentTokenInvalidError:
|
210
|
+
"""Create a payment token invalid error"""
|
211
|
+
return PaymentTokenInvalidError(token_prefix=token_prefix, reason=reason)
|
212
|
+
|
213
|
+
|
214
|
+
def create_insufficient_balance_error(current_balance: float,
|
215
|
+
required_balance: float,
|
216
|
+
token_prefix: Optional[str] = None) -> InsufficientBalanceError:
|
217
|
+
"""Create an insufficient balance error"""
|
218
|
+
return InsufficientBalanceError(
|
219
|
+
current_balance=current_balance,
|
220
|
+
required_balance=required_balance,
|
221
|
+
token_prefix=token_prefix
|
222
|
+
)
|
223
|
+
|
224
|
+
|
225
|
+
def create_charging_error(amount: float,
|
226
|
+
token_prefix: Optional[str] = None,
|
227
|
+
reason: Optional[str] = None) -> PaymentChargingError:
|
228
|
+
"""Create a payment charging error"""
|
229
|
+
return PaymentChargingError(
|
230
|
+
amount=amount,
|
231
|
+
token_prefix=token_prefix,
|
232
|
+
reason=reason
|
233
|
+
)
|
234
|
+
|
235
|
+
|
236
|
+
def create_platform_unavailable_error(operation: Optional[str] = None) -> PaymentPlatformUnavailableError:
|
237
|
+
"""Create a payment platform unavailable error"""
|
238
|
+
return PaymentPlatformUnavailableError(operation=operation)
|
239
|
+
|
240
|
+
|
241
|
+
def create_config_error(config_issue: str,
|
242
|
+
details: Optional[str] = None) -> PaymentConfigurationError:
|
243
|
+
"""Create a payment configuration error"""
|
244
|
+
return PaymentConfigurationError(config_issue=config_issue, details=details)
|
245
|
+
|
246
|
+
|
247
|
+
# Legacy compatibility - keep existing exception names but inherit from new hierarchy
|
248
|
+
class PaymentValidationError(PaymentTokenInvalidError):
|
249
|
+
"""Legacy compatibility - use PaymentTokenInvalidError instead"""
|
250
|
+
pass
|
251
|
+
|
252
|
+
|
253
|
+
class PaymentRequiredError(PaymentTokenRequiredError):
|
254
|
+
"""Legacy compatibility - use PaymentTokenRequiredError instead"""
|
255
|
+
pass
|