stls-a2a 0.1.6__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.
stls_a2a/__init__.py ADDED
@@ -0,0 +1,53 @@
1
+ from .stls_a2a_transport import SecureA2ATransport, A2AMessage
2
+ from .stls_a2a_adapters_model import AgentResponse
3
+ from .stls_a2a_integration import (
4
+ SecureA2ATransportAdapter,
5
+ create_secure_a2a_endpoint,
6
+ create_pqc_a2a_transport,
7
+ AuthConfig)
8
+
9
+ # Providers — now live in stls_core (shared with stls-mcp).
10
+ # Only "base" and "arqit_py" are supported (JVM/simple removed).
11
+ from stls_core import (
12
+ KeyProviderFactory,
13
+ KeyProvider,
14
+ KeyProviderClient,
15
+ KeyProviderServer,
16
+ KeySessionProvider,
17
+ KeySessionProviderFactory,
18
+ )
19
+
20
+ from .stls_a2a_card_model import AgentCard
21
+ from .stls_a2a_utils import (
22
+ to_a2a,
23
+ start_task_store_cleanup,
24
+ extract_a2a_text,
25
+ extract_a2a_result,
26
+ extract_a2a_artifacts)
27
+ from .stls_a2a_client import A2AClient
28
+
29
+ # Auth — now lives in stls_core (shared with stls-mcp).
30
+ from stls_core import (
31
+ ApiKeyManager,
32
+ ApiKeyManagerGroup,
33
+ default_audit_hook,
34
+ AuditEntry,
35
+ generate_api_key,
36
+ hash_key,
37
+ RedisKeyCache,
38
+ )
39
+ from stls_core.auth.stls_core_auth_loaders import (
40
+ ApiKeyLoader,
41
+ StaticLoader,
42
+ FileLoader,
43
+ PostgresLoader,
44
+ create_loader,
45
+ )
46
+
47
+ from .stls_a2a_agent_registry import AgentRegistry, make_agent_id
48
+ from .stls_a2a_remote import ContextualRemoteA2aAgent
49
+ from .stls_a2a_secure_remote import (
50
+ SecureSessionManager,
51
+ STLSHttpxTransport,
52
+ ContextualSecureRemoteA2aAgent,
53
+ )
@@ -0,0 +1,143 @@
1
+ """
2
+ Agent Adapters for A2A Secure Transport
3
+ ========================================
4
+
5
+ Common interface and adapters for different AI/Agent frameworks,
6
+ enabling secure A2A communication independent of the framework.
7
+
8
+ Supported frameworks:
9
+ - Google ADK
10
+ - LangChain
11
+ - Semantic Kernel
12
+ - Custom agents
13
+
14
+ """
15
+
16
+ from typing import Dict, Any, Optional
17
+
18
+ from .stls_a2a_adapters_model import AgentAdapter,AgentRequest,AgentResponse
19
+
20
+ from .stls_a2a_google_adapter import GoogleADKAdapter
21
+ from .stls_a2a_lc_adapter import LangChainAdapter
22
+ from .stls_a2a_sk_adapter import SemanticKernelAdapter
23
+ from .stls_a2a_custom_adapter import CustomFunctionAdapter
24
+
25
+ import logging
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ # ============================================================================
30
+ # FACTORY TO CREATE ADAPTERS
31
+ # ============================================================================
32
+
33
+ def create_adapter(
34
+ agent,
35
+ framework: str = 'auto',
36
+ **kwargs
37
+ ) -> AgentAdapter:
38
+ """
39
+ Factory to create the appropriate adapter
40
+
41
+ Args:
42
+ agent: Agent instance
43
+ framework: 'google-adk', 'langchain', 'semantic-kernel', 'custom', or 'auto'
44
+ **kwargs: Additional parameters for the adapter
45
+
46
+ Returns:
47
+ The appropriate AgentAdapter
48
+
49
+ """
50
+ if framework == 'auto':
51
+ # Framework auto-detection
52
+ agent_class = agent.__class__.__name__
53
+ module = agent.__class__.__module__
54
+
55
+ if 'google.adk' in module or 'genai.adk' in module:
56
+ framework = 'google-adk'
57
+ elif 'langchain' in module:
58
+ framework = 'langchain'
59
+ elif 'semantic_kernel' in module:
60
+ framework = 'semantic-kernel'
61
+ else:
62
+ raise ValueError(
63
+ f"Cannot auto-detect framework for {agent_class}. "
64
+ "Please specify framework explicitly."
65
+ )
66
+
67
+ # Create the appropriate adapter
68
+ if framework == 'google-adk':
69
+ return GoogleADKAdapter(agent, **kwargs)
70
+ elif framework == 'langchain':
71
+ return LangChainAdapter(agent, **kwargs)
72
+ elif framework == 'semantic-kernel':
73
+ return SemanticKernelAdapter(agent, **kwargs)
74
+ elif framework == 'custom':
75
+ return CustomFunctionAdapter(agent, **kwargs)
76
+ else:
77
+ raise ValueError(f"Unknown framework: {framework}")
78
+
79
+
80
+ # ============================================================================
81
+ # UTILITIES FOR A2A <-> AgentRequest/Response CONVERSION
82
+ # ============================================================================
83
+
84
+ def a2a_to_agent_request(a2a_message: Dict[str, Any]) -> AgentRequest:
85
+ """
86
+ Converts an A2A (JSON-RPC) message into an AgentRequest
87
+
88
+ Args:
89
+ a2a_message: A2A JSON-RPC message
90
+
91
+ Returns:
92
+ Standardized AgentRequest
93
+ """
94
+ return AgentRequest(
95
+ method=a2a_message.get('method', 'invoke'),
96
+ params=a2a_message.get('params', {}),
97
+ id=a2a_message.get('id'),
98
+ context=a2a_message.get('context')
99
+ )
100
+
101
+
102
+ def agent_response_to_a2a(response: Optional[AgentResponse]) -> Dict[str, Any]:
103
+ """
104
+ Converts an AgentResponse into an A2A (JSON-RPC) message.
105
+
106
+ Handles the response=None case (adapter that doesn't explicitly return
107
+ for unsupported methods) by returning a -32603 error instead of
108
+ raising an AttributeError.
109
+ """
110
+ if response is None:
111
+ logger.error(
112
+ "agent_response_to_a2a: received None — adapter invoke() returned no value. "
113
+ "Check that all code paths in the adapter return an AgentResponse."
114
+ )
115
+ return {
116
+ 'jsonrpc': '2.0',
117
+ 'error': {
118
+ 'code': -32603,
119
+ 'message': 'Internal error: adapter returned no response. '
120
+ 'Possible missing else-branch for unsupported method.',
121
+ },
122
+ 'id': None,
123
+ }
124
+
125
+ a2a_response: Dict[str, Any] = {
126
+ 'jsonrpc': '2.0',
127
+ 'id': response.id,
128
+ }
129
+
130
+ if response.error:
131
+ a2a_response['error'] = response.error
132
+ else:
133
+ # The result is passed as-is: normalization into A2A format
134
+ # (parts[]) already happens in CustomFunctionAdapter.invoke() for custom
135
+ # agents, and in _handle_messages_send() for all the others.
136
+ # DO NOT normalize here: Task dicts (tasks/get, tasks/cancel, messages/send)
137
+ # don't have "parts" by design and must not be transformed.
138
+ a2a_response['result'] = response.result
139
+
140
+ if response.metadata:
141
+ a2a_response['metadata'] = response.metadata
142
+
143
+ return a2a_response