agentle 0.9.4__py3-none-any.whl → 0.9.28__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.
Files changed (85) hide show
  1. agentle/agents/agent.py +175 -10
  2. agentle/agents/agent_run_output.py +8 -1
  3. agentle/agents/apis/__init__.py +79 -6
  4. agentle/agents/apis/api.py +342 -73
  5. agentle/agents/apis/api_key_authentication.py +43 -0
  6. agentle/agents/apis/api_key_location.py +11 -0
  7. agentle/agents/apis/api_metrics.py +16 -0
  8. agentle/agents/apis/auth_type.py +17 -0
  9. agentle/agents/apis/authentication.py +32 -0
  10. agentle/agents/apis/authentication_base.py +42 -0
  11. agentle/agents/apis/authentication_config.py +117 -0
  12. agentle/agents/apis/basic_authentication.py +34 -0
  13. agentle/agents/apis/bearer_authentication.py +52 -0
  14. agentle/agents/apis/cache_strategy.py +12 -0
  15. agentle/agents/apis/circuit_breaker.py +69 -0
  16. agentle/agents/apis/circuit_breaker_error.py +7 -0
  17. agentle/agents/apis/circuit_breaker_state.py +11 -0
  18. agentle/agents/apis/endpoint.py +413 -254
  19. agentle/agents/apis/file_upload.py +23 -0
  20. agentle/agents/apis/hmac_authentication.py +56 -0
  21. agentle/agents/apis/no_authentication.py +27 -0
  22. agentle/agents/apis/oauth2_authentication.py +111 -0
  23. agentle/agents/apis/oauth2_grant_type.py +12 -0
  24. agentle/agents/apis/object_schema.py +86 -1
  25. agentle/agents/apis/params/__init__.py +10 -1
  26. agentle/agents/apis/params/boolean_param.py +44 -0
  27. agentle/agents/apis/params/number_param.py +56 -0
  28. agentle/agents/apis/rate_limit_error.py +7 -0
  29. agentle/agents/apis/rate_limiter.py +57 -0
  30. agentle/agents/apis/request_config.py +126 -4
  31. agentle/agents/apis/request_hook.py +16 -0
  32. agentle/agents/apis/response_cache.py +49 -0
  33. agentle/agents/apis/retry_strategy.py +12 -0
  34. agentle/agents/whatsapp/human_delay_calculator.py +462 -0
  35. agentle/agents/whatsapp/models/audio_message.py +6 -4
  36. agentle/agents/whatsapp/models/key.py +2 -2
  37. agentle/agents/whatsapp/models/whatsapp_bot_config.py +375 -21
  38. agentle/agents/whatsapp/models/whatsapp_response_base.py +31 -0
  39. agentle/agents/whatsapp/models/whatsapp_webhook_payload.py +5 -1
  40. agentle/agents/whatsapp/providers/base/whatsapp_provider.py +51 -0
  41. agentle/agents/whatsapp/providers/evolution/evolution_api_provider.py +237 -10
  42. agentle/agents/whatsapp/providers/meta/meta_whatsapp_provider.py +126 -0
  43. agentle/agents/whatsapp/v2/batch_processor_manager.py +4 -0
  44. agentle/agents/whatsapp/v2/bot_config.py +188 -0
  45. agentle/agents/whatsapp/v2/message_limit.py +9 -0
  46. agentle/agents/whatsapp/v2/payload.py +0 -0
  47. agentle/agents/whatsapp/v2/whatsapp_bot.py +13 -0
  48. agentle/agents/whatsapp/v2/whatsapp_cloud_api_provider.py +0 -0
  49. agentle/agents/whatsapp/v2/whatsapp_provider.py +0 -0
  50. agentle/agents/whatsapp/whatsapp_bot.py +827 -45
  51. agentle/generations/providers/google/adapters/generate_generate_content_response_to_generation_adapter.py +13 -10
  52. agentle/generations/providers/google/google_generation_provider.py +35 -5
  53. agentle/generations/providers/openrouter/_adapters/openrouter_message_to_generated_assistant_message_adapter.py +35 -1
  54. agentle/mcp/servers/stdio_mcp_server.py +23 -4
  55. agentle/parsing/parsers/docx.py +8 -0
  56. agentle/parsing/parsers/file_parser.py +4 -0
  57. agentle/parsing/parsers/pdf.py +7 -1
  58. agentle/storage/__init__.py +11 -0
  59. agentle/storage/file_storage_manager.py +44 -0
  60. agentle/storage/local_file_storage_manager.py +122 -0
  61. agentle/storage/s3_file_storage_manager.py +124 -0
  62. agentle/tts/audio_format.py +6 -0
  63. agentle/tts/elevenlabs_tts_provider.py +108 -0
  64. agentle/tts/output_format_type.py +26 -0
  65. agentle/tts/speech_config.py +14 -0
  66. agentle/tts/speech_result.py +15 -0
  67. agentle/tts/tts_provider.py +16 -0
  68. agentle/tts/voice_settings.py +30 -0
  69. agentle/utils/parse_streaming_json.py +39 -13
  70. agentle/voice_cloning/__init__.py +0 -0
  71. agentle/voice_cloning/voice_cloner.py +0 -0
  72. agentle/web/extractor.py +282 -148
  73. {agentle-0.9.4.dist-info → agentle-0.9.28.dist-info}/METADATA +1 -1
  74. {agentle-0.9.4.dist-info → agentle-0.9.28.dist-info}/RECORD +78 -39
  75. agentle/tts/real_time/definitions/audio_data.py +0 -20
  76. agentle/tts/real_time/definitions/speech_config.py +0 -27
  77. agentle/tts/real_time/definitions/speech_result.py +0 -14
  78. agentle/tts/real_time/definitions/tts_stream_chunk.py +0 -15
  79. agentle/tts/real_time/definitions/voice_gender.py +0 -9
  80. agentle/tts/real_time/definitions/voice_info.py +0 -18
  81. agentle/tts/real_time/real_time_speech_to_text_provider.py +0 -66
  82. /agentle/{tts/real_time → agents/whatsapp/v2}/__init__.py +0 -0
  83. /agentle/{tts/real_time/definitions/__init__.py → agents/whatsapp/v2/in_memory_batch_processor_manager.py} +0 -0
  84. {agentle-0.9.4.dist-info → agentle-0.9.28.dist-info}/WHEEL +0 -0
  85. {agentle-0.9.4.dist-info → agentle-0.9.28.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,16 @@
1
+ """API metrics tracking."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from rsb.models.base_model import BaseModel
6
+
7
+
8
+ class APIMetrics(BaseModel):
9
+ """Metrics for API usage."""
10
+
11
+ total_requests: int = 0
12
+ successful_requests: int = 0
13
+ failed_requests: int = 0
14
+ total_latency_ms: float = 0.0
15
+ average_latency_ms: float = 0.0
16
+ requests_by_endpoint: dict[str, int] = {}
@@ -0,0 +1,17 @@
1
+ """Authentication types."""
2
+
3
+ from enum import StrEnum
4
+
5
+
6
+ class AuthType(StrEnum):
7
+ """Types of authentication supported."""
8
+
9
+ NONE = "none"
10
+ BEARER = "bearer"
11
+ BASIC = "basic"
12
+ API_KEY = "api_key"
13
+ OAUTH2 = "oauth2"
14
+ JWT = "jwt"
15
+ CUSTOM = "custom"
16
+ AWS_SIGNATURE = "aws_signature"
17
+ HMAC = "hmac"
@@ -0,0 +1,32 @@
1
+ """
2
+ Authentication support for API endpoints.
3
+
4
+ Provides various authentication methods including Bearer, Basic, OAuth2, API Key, and custom schemes.
5
+ """
6
+
7
+ # Import and re-export for backward compatibility
8
+ from agentle.agents.apis.api_key_location import ApiKeyLocation
9
+ from agentle.agents.apis.api_key_authentication import ApiKeyAuthentication
10
+ from agentle.agents.apis.auth_type import AuthType
11
+ from agentle.agents.apis.authentication_base import AuthenticationBase
12
+ from agentle.agents.apis.authentication_config import AuthenticationConfig
13
+ from agentle.agents.apis.basic_authentication import BasicAuthentication
14
+ from agentle.agents.apis.bearer_authentication import BearerAuthentication
15
+ from agentle.agents.apis.hmac_authentication import HMACAuthentication
16
+ from agentle.agents.apis.no_authentication import NoAuthentication
17
+ from agentle.agents.apis.oauth2_authentication import OAuth2Authentication
18
+ from agentle.agents.apis.oauth2_grant_type import OAuth2GrantType
19
+
20
+ __all__ = [
21
+ "AuthType",
22
+ "ApiKeyLocation",
23
+ "OAuth2GrantType",
24
+ "AuthenticationBase",
25
+ "NoAuthentication",
26
+ "BearerAuthentication",
27
+ "BasicAuthentication",
28
+ "ApiKeyAuthentication",
29
+ "OAuth2Authentication",
30
+ "HMACAuthentication",
31
+ "AuthenticationConfig",
32
+ ]
@@ -0,0 +1,42 @@
1
+ """Base authentication handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from collections.abc import MutableMapping
7
+ from typing import Any
8
+
9
+ import aiohttp
10
+
11
+
12
+ class AuthenticationBase(ABC):
13
+ """Base class for authentication handlers."""
14
+
15
+ @abstractmethod
16
+ async def apply_auth(
17
+ self,
18
+ session: aiohttp.ClientSession,
19
+ url: str,
20
+ headers: MutableMapping[str, str],
21
+ params: MutableMapping[str, Any],
22
+ ) -> None:
23
+ """
24
+ Apply authentication to the request.
25
+
26
+ Args:
27
+ session: aiohttp session
28
+ url: Request URL
29
+ headers: Request headers (will be modified)
30
+ params: Request parameters (will be modified)
31
+ """
32
+ pass
33
+
34
+ @abstractmethod
35
+ async def refresh_if_needed(self) -> bool:
36
+ """
37
+ Refresh authentication if needed (e.g., expired tokens).
38
+
39
+ Returns:
40
+ True if refresh was performed, False otherwise
41
+ """
42
+ pass
@@ -0,0 +1,117 @@
1
+ """Authentication configuration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import MutableMapping
6
+
7
+ from rsb.models.base_model import BaseModel
8
+ from rsb.models.field import Field
9
+
10
+ from agentle.agents.apis.api_key_authentication import ApiKeyAuthentication
11
+ from agentle.agents.apis.api_key_location import ApiKeyLocation
12
+ from agentle.agents.apis.auth_type import AuthType
13
+ from agentle.agents.apis.authentication_base import AuthenticationBase
14
+ from agentle.agents.apis.basic_authentication import BasicAuthentication
15
+ from agentle.agents.apis.bearer_authentication import BearerAuthentication
16
+ from agentle.agents.apis.hmac_authentication import HMACAuthentication
17
+ from agentle.agents.apis.no_authentication import NoAuthentication
18
+ from agentle.agents.apis.oauth2_authentication import OAuth2Authentication
19
+ from agentle.agents.apis.oauth2_grant_type import OAuth2GrantType
20
+
21
+
22
+ class AuthenticationConfig(BaseModel):
23
+ """Configuration for API authentication."""
24
+
25
+ type: AuthType = Field(default=AuthType.NONE)
26
+
27
+ # Bearer token
28
+ bearer_token: str | None = Field(default=None)
29
+
30
+ # Basic auth
31
+ username: str | None = Field(default=None)
32
+ password: str | None = Field(default=None)
33
+
34
+ # API Key
35
+ api_key: str | None = Field(default=None)
36
+ api_key_location: ApiKeyLocation = Field(default=ApiKeyLocation.HEADER)
37
+ api_key_name: str = Field(default="X-API-Key")
38
+
39
+ # OAuth2
40
+ oauth2_token_url: str | None = Field(default=None)
41
+ oauth2_client_id: str | None = Field(default=None)
42
+ oauth2_client_secret: str | None = Field(default=None)
43
+ oauth2_grant_type: OAuth2GrantType = Field(
44
+ default=OAuth2GrantType.CLIENT_CREDENTIALS
45
+ )
46
+ oauth2_scope: str | None = Field(
47
+ default=None,
48
+ description="Single scope string (deprecated, use oauth2_scopes for multiple)",
49
+ )
50
+ oauth2_scopes: list[str] | None = Field(
51
+ default=None,
52
+ description="List of OAuth2 scopes to request (e.g., ['read', 'write', 'admin'])",
53
+ )
54
+ oauth2_refresh_token: str | None = Field(default=None)
55
+
56
+ # HMAC
57
+ hmac_secret_key: str | None = Field(default=None)
58
+ hmac_algorithm: str = Field(default="sha256")
59
+ hmac_header_name: str = Field(default="X-Signature")
60
+
61
+ # Custom
62
+ custom_headers: MutableMapping[str, str] = Field(default_factory=dict)
63
+
64
+ def create_handler(self) -> AuthenticationBase:
65
+ """Create authentication handler from config."""
66
+ if self.type == AuthType.NONE:
67
+ return NoAuthentication()
68
+
69
+ elif self.type == AuthType.BEARER:
70
+ if not self.bearer_token:
71
+ raise ValueError("Bearer token required for Bearer authentication")
72
+ return BearerAuthentication(self.bearer_token)
73
+
74
+ elif self.type == AuthType.BASIC:
75
+ if not self.username or not self.password:
76
+ raise ValueError(
77
+ "Username and password required for Basic authentication"
78
+ )
79
+ return BasicAuthentication(self.username, self.password)
80
+
81
+ elif self.type == AuthType.API_KEY:
82
+ if not self.api_key:
83
+ raise ValueError("API key required for API Key authentication")
84
+ return ApiKeyAuthentication(
85
+ self.api_key, self.api_key_location, self.api_key_name
86
+ )
87
+
88
+ elif self.type == AuthType.OAUTH2:
89
+ if not all(
90
+ [
91
+ self.oauth2_token_url,
92
+ self.oauth2_client_id,
93
+ self.oauth2_client_secret,
94
+ ]
95
+ ):
96
+ raise ValueError(
97
+ "OAuth2 credentials required for OAuth2 authentication"
98
+ )
99
+ return OAuth2Authentication(
100
+ self.oauth2_token_url, # type: ignore
101
+ self.oauth2_client_id, # type: ignore
102
+ self.oauth2_client_secret, # type: ignore
103
+ self.oauth2_grant_type,
104
+ self.oauth2_scope,
105
+ self.oauth2_refresh_token,
106
+ self.oauth2_scopes,
107
+ )
108
+
109
+ elif self.type == AuthType.HMAC:
110
+ if not self.hmac_secret_key:
111
+ raise ValueError("HMAC secret key required for HMAC authentication")
112
+ return HMACAuthentication(
113
+ self.hmac_secret_key, self.hmac_algorithm, self.hmac_header_name
114
+ )
115
+
116
+ else:
117
+ return NoAuthentication()
@@ -0,0 +1,34 @@
1
+ """Basic authentication."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ from collections.abc import MutableMapping
7
+ from typing import Any
8
+
9
+ import aiohttp
10
+ from agentle.agents.apis.authentication_base import AuthenticationBase
11
+
12
+
13
+ class BasicAuthentication(AuthenticationBase):
14
+ """HTTP Basic authentication."""
15
+
16
+ def __init__(self, username: str, password: str):
17
+ self.username = username
18
+ self.password = password
19
+
20
+ async def apply_auth(
21
+ self,
22
+ session: aiohttp.ClientSession,
23
+ url: str,
24
+ headers: MutableMapping[str, str],
25
+ params: MutableMapping[str, Any],
26
+ ) -> None:
27
+ """Add Basic auth to Authorization header."""
28
+ credentials = f"{self.username}:{self.password}"
29
+ encoded = base64.b64encode(credentials.encode()).decode()
30
+ headers["Authorization"] = f"Basic {encoded}"
31
+
32
+ async def refresh_if_needed(self) -> bool:
33
+ """No refresh needed for Basic auth."""
34
+ return False
@@ -0,0 +1,52 @@
1
+ """Bearer token authentication."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import MutableMapping
6
+ from datetime import datetime, timedelta
7
+ from typing import Any
8
+
9
+ import aiohttp
10
+ from agentle.agents.apis.authentication_base import AuthenticationBase
11
+
12
+
13
+ class BearerAuthentication(AuthenticationBase):
14
+ """Bearer token authentication."""
15
+
16
+ def __init__(self, token: str, auto_refresh: bool = False):
17
+ self.token = token
18
+ self.auto_refresh = auto_refresh
19
+ self._token_expiry: datetime | None = None
20
+
21
+ async def apply_auth(
22
+ self,
23
+ session: aiohttp.ClientSession,
24
+ url: str,
25
+ headers: MutableMapping[str, str],
26
+ params: MutableMapping[str, Any],
27
+ ) -> None:
28
+ """Add Bearer token to Authorization header."""
29
+ headers["Authorization"] = f"Bearer {self.token}"
30
+
31
+ async def refresh_if_needed(self) -> bool:
32
+ """Check if token needs refresh."""
33
+ if not self.auto_refresh:
34
+ return False
35
+
36
+ if self._token_expiry and datetime.now() >= self._token_expiry:
37
+ # Token expired - subclass should implement refresh logic
38
+ return False
39
+
40
+ return False
41
+
42
+ def set_token(self, token: str, expires_in: int | None = None) -> None:
43
+ """
44
+ Update the token.
45
+
46
+ Args:
47
+ token: New token
48
+ expires_in: Token expiry in seconds
49
+ """
50
+ self.token = token
51
+ if expires_in:
52
+ self._token_expiry = datetime.now() + timedelta(seconds=expires_in)
@@ -0,0 +1,12 @@
1
+ """Cache strategies."""
2
+
3
+ from enum import StrEnum
4
+
5
+
6
+ class CacheStrategy(StrEnum):
7
+ """Cache strategies."""
8
+
9
+ NONE = "none"
10
+ MEMORY = "memory"
11
+ REDIS = "redis"
12
+ CUSTOM = "custom"
@@ -0,0 +1,69 @@
1
+ """Circuit breaker for resilient API calls.
2
+
3
+ This module adapts the resilience module's circuit breaker implementations
4
+ for use in the APIs module, maintaining backward compatibility.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Callable
10
+ from typing import Any
11
+
12
+ from agentle.agents.apis.circuit_breaker_error import CircuitBreakerError
13
+ from agentle.agents.apis.request_config import RequestConfig
14
+ from agentle.resilience.circuit_breaker.in_memory_circuit_breaker import (
15
+ InMemoryCircuitBreaker,
16
+ )
17
+
18
+
19
+ class CircuitBreaker:
20
+ """
21
+ Circuit breaker implementation for resilient API calls.
22
+
23
+ This wraps the resilience module's InMemoryCircuitBreaker to provide
24
+ a simpler call-based API for endpoint usage.
25
+ """
26
+
27
+ def __init__(self, config: RequestConfig):
28
+ self.config = config
29
+ self._circuit_id = "default" # Single circuit per endpoint
30
+ # Initialize the underlying circuit breaker from resilience module
31
+ self._impl = InMemoryCircuitBreaker(
32
+ failure_threshold=config.circuit_breaker_failure_threshold,
33
+ recovery_timeout=config.circuit_breaker_recovery_timeout,
34
+ half_open_success_threshold=config.circuit_breaker_success_threshold,
35
+ enable_metrics=config.enable_metrics,
36
+ )
37
+
38
+ async def call(self, func: Callable[[], Any]) -> Any:
39
+ """
40
+ Execute function with circuit breaker protection.
41
+
42
+ Args:
43
+ func: Async function to execute
44
+
45
+ Returns:
46
+ Result of func call
47
+
48
+ Raises:
49
+ CircuitBreakerError: If circuit is open
50
+ """
51
+ # Check if circuit is open
52
+ if await self._impl.is_open(self._circuit_id):
53
+ # Get circuit state for more details
54
+ state = await self._impl.get_circuit_state(self._circuit_id)
55
+ next_retry_seconds = state.get("next_recovery_attempt_in_seconds", 0)
56
+
57
+ if next_retry_seconds > 0:
58
+ raise CircuitBreakerError(
59
+ f"Circuit breaker is OPEN. Retry after {next_retry_seconds:.1f}s"
60
+ )
61
+
62
+ # Execute the function
63
+ try:
64
+ result = await func()
65
+ await self._impl.record_success(self._circuit_id)
66
+ return result
67
+ except Exception:
68
+ await self._impl.record_failure(self._circuit_id)
69
+ raise
@@ -0,0 +1,7 @@
1
+ """Circuit breaker errors."""
2
+
3
+
4
+ class CircuitBreakerError(Exception):
5
+ """Raised when circuit breaker is open."""
6
+
7
+ pass
@@ -0,0 +1,11 @@
1
+ """Circuit breaker states."""
2
+
3
+ from enum import StrEnum
4
+
5
+
6
+ class CircuitBreakerState(StrEnum):
7
+ """Circuit breaker states."""
8
+
9
+ CLOSED = "closed" # Normal operation
10
+ OPEN = "open" # Failing, reject requests
11
+ HALF_OPEN = "half_open" # Testing if service recovered