cloud-dog-api-kit 0.13.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.
Files changed (98) hide show
  1. cloud_dog_api_kit/__init__.py +170 -0
  2. cloud_dog_api_kit/a2a/__init__.py +53 -0
  3. cloud_dog_api_kit/a2a/card.py +138 -0
  4. cloud_dog_api_kit/a2a/events.py +1123 -0
  5. cloud_dog_api_kit/a2a/gateway.py +105 -0
  6. cloud_dog_api_kit/a2a/skill_audit.py +107 -0
  7. cloud_dog_api_kit/auth/__init__.py +35 -0
  8. cloud_dog_api_kit/auth/dependency.py +121 -0
  9. cloud_dog_api_kit/auth/rbac.py +107 -0
  10. cloud_dog_api_kit/auth/service_auth.py +54 -0
  11. cloud_dog_api_kit/clients/__init__.py +29 -0
  12. cloud_dog_api_kit/clients/circuit_breaker.py +39 -0
  13. cloud_dog_api_kit/clients/http_client.py +127 -0
  14. cloud_dog_api_kit/clients/retry.py +83 -0
  15. cloud_dog_api_kit/compat/__init__.py +37 -0
  16. cloud_dog_api_kit/compat/envelope.py +120 -0
  17. cloud_dog_api_kit/compat/profile.py +102 -0
  18. cloud_dog_api_kit/compat/routes.py +90 -0
  19. cloud_dog_api_kit/config.py +54 -0
  20. cloud_dog_api_kit/correlation/__init__.py +50 -0
  21. cloud_dog_api_kit/correlation/context.py +118 -0
  22. cloud_dog_api_kit/correlation/middleware.py +133 -0
  23. cloud_dog_api_kit/envelopes/__init__.py +37 -0
  24. cloud_dog_api_kit/envelopes/error.py +87 -0
  25. cloud_dog_api_kit/envelopes/success.py +84 -0
  26. cloud_dog_api_kit/errors/__init__.py +51 -0
  27. cloud_dog_api_kit/errors/exceptions.py +184 -0
  28. cloud_dog_api_kit/errors/handler.py +102 -0
  29. cloud_dog_api_kit/errors/taxonomy.py +62 -0
  30. cloud_dog_api_kit/factory.py +157 -0
  31. cloud_dog_api_kit/idempotency/__init__.py +28 -0
  32. cloud_dog_api_kit/idempotency/middleware.py +118 -0
  33. cloud_dog_api_kit/idempotency/store.py +100 -0
  34. cloud_dog_api_kit/lifecycle/__init__.py +39 -0
  35. cloud_dog_api_kit/lifecycle/hooks.py +75 -0
  36. cloud_dog_api_kit/lifecycle/shutdown.py +178 -0
  37. cloud_dog_api_kit/mcp/__init__.py +122 -0
  38. cloud_dog_api_kit/mcp/async_jobs.py +126 -0
  39. cloud_dog_api_kit/mcp/client_sdk.py +235 -0
  40. cloud_dog_api_kit/mcp/client_transport/__init__.py +47 -0
  41. cloud_dog_api_kit/mcp/client_transport/base.py +98 -0
  42. cloud_dog_api_kit/mcp/client_transport/exceptions.py +37 -0
  43. cloud_dog_api_kit/mcp/client_transport/http_jsonrpc.py +405 -0
  44. cloud_dog_api_kit/mcp/client_transport/legacy_sse.py +320 -0
  45. cloud_dog_api_kit/mcp/client_transport/stdio.py +322 -0
  46. cloud_dog_api_kit/mcp/client_transport/streamable_http.py +748 -0
  47. cloud_dog_api_kit/mcp/contract.py +113 -0
  48. cloud_dog_api_kit/mcp/error_mapper.py +84 -0
  49. cloud_dog_api_kit/mcp/gateway.py +117 -0
  50. cloud_dog_api_kit/mcp/legacy_sse.py +129 -0
  51. cloud_dog_api_kit/mcp/session.py +96 -0
  52. cloud_dog_api_kit/mcp/sync_handler.py +269 -0
  53. cloud_dog_api_kit/mcp/tool_audit.py +136 -0
  54. cloud_dog_api_kit/mcp/tool_router.py +180 -0
  55. cloud_dog_api_kit/mcp/transport.py +1041 -0
  56. cloud_dog_api_kit/middleware/__init__.py +39 -0
  57. cloud_dog_api_kit/middleware/cors.py +74 -0
  58. cloud_dog_api_kit/middleware/logging.py +98 -0
  59. cloud_dog_api_kit/middleware/request_size_limit.py +86 -0
  60. cloud_dog_api_kit/middleware/timeout.py +78 -0
  61. cloud_dog_api_kit/middleware/timing.py +52 -0
  62. cloud_dog_api_kit/openapi/__init__.py +30 -0
  63. cloud_dog_api_kit/openapi/customise.py +69 -0
  64. cloud_dog_api_kit/openapi/route.py +46 -0
  65. cloud_dog_api_kit/routers/__init__.py +41 -0
  66. cloud_dog_api_kit/routers/crud.py +173 -0
  67. cloud_dog_api_kit/routers/health.py +160 -0
  68. cloud_dog_api_kit/routers/jobs.py +69 -0
  69. cloud_dog_api_kit/routers/version.py +46 -0
  70. cloud_dog_api_kit/schemas/__init__.py +36 -0
  71. cloud_dog_api_kit/schemas/envelopes.py +37 -0
  72. cloud_dog_api_kit/schemas/filters.py +103 -0
  73. cloud_dog_api_kit/schemas/pagination.py +148 -0
  74. cloud_dog_api_kit/streaming/__init__.py +28 -0
  75. cloud_dog_api_kit/streaming/events.py +47 -0
  76. cloud_dog_api_kit/streaming/jsonl.py +68 -0
  77. cloud_dog_api_kit/streaming/sse.py +102 -0
  78. cloud_dog_api_kit/testing/__init__.py +46 -0
  79. cloud_dog_api_kit/testing/conformance.py +156 -0
  80. cloud_dog_api_kit/testing/fixtures.py +90 -0
  81. cloud_dog_api_kit/testing/flows/__init__.py +32 -0
  82. cloud_dog_api_kit/testing/flows/auth_flow.py +41 -0
  83. cloud_dog_api_kit/testing/flows/crud_flow.py +50 -0
  84. cloud_dog_api_kit/testing/flows/job_flow.py +42 -0
  85. cloud_dog_api_kit/testing/flows/streaming_flow.py +42 -0
  86. cloud_dog_api_kit/traceability_ids.py +84 -0
  87. cloud_dog_api_kit/versioning/__init__.py +30 -0
  88. cloud_dog_api_kit/versioning/header.py +52 -0
  89. cloud_dog_api_kit/web/__init__.py +7 -0
  90. cloud_dog_api_kit/web/proxy.py +222 -0
  91. cloud_dog_api_kit/webhook/__init__.py +29 -0
  92. cloud_dog_api_kit/webhook/signature.py +149 -0
  93. cloud_dog_api_kit-0.13.0.dist-info/METADATA +27 -0
  94. cloud_dog_api_kit-0.13.0.dist-info/RECORD +98 -0
  95. cloud_dog_api_kit-0.13.0.dist-info/WHEEL +4 -0
  96. cloud_dog_api_kit-0.13.0.dist-info/licenses/LICENCE +190 -0
  97. cloud_dog_api_kit-0.13.0.dist-info/licenses/LICENSE +176 -0
  98. cloud_dog_api_kit-0.13.0.dist-info/licenses/NOTICE +7 -0
@@ -0,0 +1,170 @@
1
+ # Copyright 2026 Cloud-Dog, Viewdeck Engineering Limited
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # cloud_dog_api_kit — Public API
16
+ #
17
+ # Licence: Proprietary — Cloud-Dog AI Platform
18
+ # Owner: Cloud-Dog AI
19
+ # Description: PS-20 API toolkit for all Cloud-Dog Python services. Provides
20
+ # standard envelopes, error taxonomy, auth, CRUD, pagination, health,
21
+ # streaming, HTTP client, correlation, idempotency, CORS, timeout,
22
+ # OpenAPI, MCP/A2A gateways, test scaffolding, and an app factory.
23
+ # Related requirements: SV1.1, SV1.2, FR17.1
24
+ # Related architecture: SA1
25
+
26
+ """cloud_dog_api_kit — PS-20 API toolkit for Cloud-Dog services.
27
+
28
+ Public API:
29
+ create_app(...) — FastAPI app factory wiring all components.
30
+ """
31
+
32
+ # ruff: noqa: E402
33
+
34
+ from __future__ import annotations
35
+
36
+ __version__ = "0.12.1"
37
+
38
+
39
+ def create_app(*args, **kwargs): # type: ignore[no-untyped-def]
40
+ """Import the app factory lazily so optional dependencies remain optional."""
41
+ try:
42
+ from cloud_dog_api_kit.factory import create_app as _create_app
43
+ except ModuleNotFoundError as exc:
44
+ if exc.name != "cloud_dog_logging":
45
+ raise
46
+ raise ModuleNotFoundError("create_app requires the optional 'cloud-dog-logging' dependency") from exc
47
+ return _create_app(*args, **kwargs)
48
+
49
+
50
+ from cloud_dog_api_kit.a2a.events import (
51
+ ConfigChangeEvent,
52
+ EventBroadcaster,
53
+ HTTPIngestAdapter,
54
+ InMemoryEventBroadcaster,
55
+ PersistentEventBroadcaster,
56
+ RESTPollAdapter,
57
+ WebSocketAdapter,
58
+ create_a2a_events_router,
59
+ )
60
+ from cloud_dog_api_kit.compat import (
61
+ LegacyEnvelopeMiddleware,
62
+ LegacyRouteAdapter,
63
+ LegacyRouteAdapterMiddleware,
64
+ ProfileContextMiddleware,
65
+ legacy_envelope_route,
66
+ )
67
+ from cloud_dog_api_kit.correlation import CorrelationMiddleware
68
+ from cloud_dog_api_kit.middleware import configure_cors
69
+ from cloud_dog_api_kit.lifecycle import (
70
+ GracefulShutdownManager,
71
+ LifecycleHooks,
72
+ ShutdownDrainMiddleware,
73
+ install_shutdown_signal_handlers,
74
+ )
75
+ from cloud_dog_api_kit.mcp import (
76
+ MCPContractRegistration,
77
+ McpSessionManager,
78
+ ToolContract,
79
+ map_legacy_mcp_payload,
80
+ register_mcp_contract,
81
+ register_mcp_routes,
82
+ register_tool_router,
83
+ )
84
+ from cloud_dog_api_kit.envelopes import (
85
+ ErrorDetail,
86
+ ErrorResponse,
87
+ Meta,
88
+ SuccessResponse,
89
+ error_envelope,
90
+ success_envelope,
91
+ )
92
+ from cloud_dog_api_kit.schemas.pagination import PageInfo, PaginatedData, paginated_envelope
93
+ from cloud_dog_api_kit.errors import (
94
+ APIError,
95
+ ConflictError,
96
+ InternalError,
97
+ NotFoundError,
98
+ RateLimitError,
99
+ TimeoutError,
100
+ UnauthenticatedError,
101
+ UnauthorisedError,
102
+ UpstreamError,
103
+ ValidationError,
104
+ register_error_handlers,
105
+ )
106
+ from cloud_dog_api_kit.routers.health import HealthCheck, create_health_router
107
+ from cloud_dog_api_kit.middleware import RequestLoggingMiddleware, RequestSizeLimitMiddleware
108
+ from cloud_dog_api_kit.openapi import configure_openapi
109
+ from cloud_dog_api_kit.versioning import VersionHeaderMiddleware
110
+ from cloud_dog_api_kit.webhook import WebhookSignatureMiddleware, compute_webhook_signature
111
+ from cloud_dog_api_kit.web.proxy import WebApiProxy
112
+
113
+ __all__ = [
114
+ "create_app",
115
+ "APIError",
116
+ "ConfigChangeEvent",
117
+ "ConflictError",
118
+ "EventBroadcaster",
119
+ "HTTPIngestAdapter",
120
+ "InMemoryEventBroadcaster",
121
+ "PersistentEventBroadcaster",
122
+ "RESTPollAdapter",
123
+ "WebSocketAdapter",
124
+ "create_a2a_events_router",
125
+ "InternalError",
126
+ "NotFoundError",
127
+ "RateLimitError",
128
+ "TimeoutError",
129
+ "UnauthenticatedError",
130
+ "UnauthorisedError",
131
+ "UpstreamError",
132
+ "ValidationError",
133
+ "GracefulShutdownManager",
134
+ "ErrorDetail",
135
+ "ErrorResponse",
136
+ "LegacyEnvelopeMiddleware",
137
+ "LegacyRouteAdapter",
138
+ "LegacyRouteAdapterMiddleware",
139
+ "LifecycleHooks",
140
+ "MCPContractRegistration",
141
+ "Meta",
142
+ "McpSessionManager",
143
+ "PageInfo",
144
+ "PaginatedData",
145
+ "ProfileContextMiddleware",
146
+ "RequestSizeLimitMiddleware",
147
+ "ShutdownDrainMiddleware",
148
+ "SuccessResponse",
149
+ "ToolContract",
150
+ "WebhookSignatureMiddleware",
151
+ "compute_webhook_signature",
152
+ "error_envelope",
153
+ "install_shutdown_signal_handlers",
154
+ "legacy_envelope_route",
155
+ "map_legacy_mcp_payload",
156
+ "paginated_envelope",
157
+ "register_mcp_contract",
158
+ "register_mcp_routes",
159
+ "success_envelope",
160
+ "register_tool_router",
161
+ "register_error_handlers",
162
+ "HealthCheck",
163
+ "create_health_router",
164
+ "CorrelationMiddleware",
165
+ "RequestLoggingMiddleware",
166
+ "VersionHeaderMiddleware",
167
+ "configure_cors",
168
+ "configure_openapi",
169
+ "WebApiProxy",
170
+ ]
@@ -0,0 +1,53 @@
1
+ # Copyright 2026 Cloud-Dog, Viewdeck Engineering Limited
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # cloud_dog_api_kit — A2A gateway
16
+ #
17
+ # Licence: Proprietary — Cloud-Dog AI Platform
18
+ # Owner: Cloud-Dog AI
19
+ # Description: REST-to-A2A gateway helpers.
20
+ # Related requirements: FR15.1
21
+ # Related architecture: SA1
22
+
23
+ """A2A gateway helpers for cloud_dog_api_kit."""
24
+
25
+ from __future__ import annotations
26
+
27
+ from cloud_dog_api_kit.a2a.card import A2ASkill, create_a2a_card_router
28
+ from cloud_dog_api_kit.a2a.events import (
29
+ ConfigChangeEvent,
30
+ EventBroadcaster,
31
+ HTTPIngestAdapter,
32
+ InMemoryEventBroadcaster,
33
+ PersistentEventBroadcaster,
34
+ RESTPollAdapter,
35
+ WebSocketAdapter,
36
+ create_a2a_events_router,
37
+ )
38
+ from cloud_dog_api_kit.a2a.gateway import A2AHandler, create_a2a_handler_from_endpoint
39
+
40
+ __all__ = [
41
+ "A2AHandler",
42
+ "A2ASkill",
43
+ "ConfigChangeEvent",
44
+ "EventBroadcaster",
45
+ "HTTPIngestAdapter",
46
+ "InMemoryEventBroadcaster",
47
+ "PersistentEventBroadcaster",
48
+ "RESTPollAdapter",
49
+ "WebSocketAdapter",
50
+ "create_a2a_card_router",
51
+ "create_a2a_events_router",
52
+ "create_a2a_handler_from_endpoint",
53
+ ]
@@ -0,0 +1,138 @@
1
+ # Copyright 2026 Cloud-Dog, Viewdeck Engineering Limited
2
+ # Licensed under the Apache License, Version 2.0
3
+
4
+ """A2A agent card and task submission router.
5
+
6
+ Provides ``create_a2a_card_router`` which adds:
7
+ - ``GET /.well-known/agent.json`` — agent card per A2A protocol spec
8
+ - ``POST /a2a/tasks`` — task submission that routes to skill handlers
9
+
10
+ Usage in any service's A2A server::
11
+
12
+ from cloud_dog_api_kit.a2a.card import create_a2a_card_router, A2ASkill
13
+
14
+ skills = [
15
+ A2ASkill(id="search", name="Search", description="Semantic search",
16
+ handler=lambda text: service.search(query=text)),
17
+ ]
18
+ router = create_a2a_card_router(
19
+ name="index-retriever",
20
+ description="Vector database search and document ingestion",
21
+ url="https://example.invalid/a2a",
22
+ skills=skills,
23
+ )
24
+ app.include_router(router)
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from dataclasses import dataclass, field
30
+ from typing import Any, Callable
31
+ from uuid import uuid4
32
+
33
+ from fastapi import APIRouter, Request
34
+ from fastapi.responses import JSONResponse
35
+
36
+
37
+ @dataclass
38
+ class A2ASkill:
39
+ """A skill exposed via the A2A agent card."""
40
+
41
+ id: str
42
+ name: str
43
+ description: str
44
+ handler: Callable[[str], Any] | None = None
45
+
46
+
47
+ def create_a2a_card_router(
48
+ *,
49
+ name: str,
50
+ description: str,
51
+ url: str = "",
52
+ version: str = "1.0.0",
53
+ skills: list[A2ASkill] | None = None,
54
+ ) -> APIRouter:
55
+ """Create a FastAPI router with A2A agent card and task submission.
56
+
57
+ Args:
58
+ name: Service name for the agent card.
59
+ description: Human-readable service description.
60
+ url: Base URL for the A2A endpoint.
61
+ version: Service version string.
62
+ skills: List of skills to expose. Each skill can have a handler
63
+ function that accepts a text input and returns a result.
64
+
65
+ Returns:
66
+ A FastAPI APIRouter with agent card and task routes.
67
+ """
68
+ _skills = skills or []
69
+ _skill_map: dict[str, A2ASkill] = {s.id: s for s in _skills}
70
+
71
+ card = {
72
+ "name": name,
73
+ "description": description,
74
+ "url": url,
75
+ "version": version,
76
+ "capabilities": {"streaming": False, "pushNotifications": False},
77
+ "skills": [
78
+ {"id": s.id, "name": s.name, "description": s.description}
79
+ for s in _skills
80
+ ],
81
+ }
82
+
83
+ router = APIRouter()
84
+
85
+ @router.get("/.well-known/agent.json")
86
+ async def agent_card() -> JSONResponse:
87
+ """Return the A2A agent card as JSON."""
88
+ return JSONResponse(card)
89
+
90
+ @router.post("/a2a/tasks")
91
+ @router.post("/tasks")
92
+ async def submit_task(request: Request) -> JSONResponse:
93
+ """Accept an A2A task submission and dispatch to the matching skill."""
94
+ body = await request.json()
95
+ task_id = body.get("id", str(uuid4()))
96
+ skill_id = body.get("skill_id", "")
97
+ input_data = body.get("input", {})
98
+ input_text = input_data.get("text", "") if isinstance(input_data, dict) else str(input_data)
99
+
100
+ skill = _skill_map.get(skill_id)
101
+ if skill is None:
102
+ # If no specific skill, try a generic "health" skill
103
+ if skill_id == "health":
104
+ return JSONResponse({
105
+ "id": task_id,
106
+ "status": "completed",
107
+ "output": {"type": "text", "text": f"{name} is healthy"},
108
+ })
109
+ return JSONResponse(
110
+ {"id": task_id, "status": "failed",
111
+ "error": f"Unknown skill: {skill_id}. Available: {list(_skill_map.keys())}"},
112
+ status_code=404,
113
+ )
114
+
115
+ try:
116
+ if skill.handler is not None:
117
+ result = skill.handler(input_text)
118
+ # Handle async handlers
119
+ import asyncio
120
+ if asyncio.iscoroutine(result):
121
+ result = await result
122
+ result_text = str(result)
123
+ else:
124
+ result_text = f"Skill '{skill_id}' acknowledged (no handler configured)"
125
+ except Exception as exc:
126
+ return JSONResponse({
127
+ "id": task_id,
128
+ "status": "failed",
129
+ "error": str(exc),
130
+ })
131
+
132
+ return JSONResponse({
133
+ "id": task_id,
134
+ "status": "completed",
135
+ "output": {"type": "text", "text": result_text},
136
+ })
137
+
138
+ return router