squidcloud-backend 1.0.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: squidcloud-backend
3
+ Version: 1.0.0
4
+ Summary: Squid Cloud Python backend SDK — decorators, services, and project entry point
5
+ Author-email: Squid Cloud <support@squid.cloud>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://squid.cloud
8
+ Project-URL: Documentation, https://docs.squid.cloud
9
+ Project-URL: Repository, https://github.com/squid-cloud/squid-cloud
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: squidcloud-client>=1.0.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7.0; extra == "dev"
20
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
21
+ Requires-Dist: ruff>=0.11.0; extra == "dev"
@@ -0,0 +1,138 @@
1
+ # Squid Cloud Python Backend SDK
2
+
3
+ Python equivalent of `@squidcloud/backend` (the TypeScript backend SDK).
4
+ Provides decorators, `SquidService`, `SquidProject`, and types for building
5
+ Squid backend services in Python.
6
+
7
+ ## Package: `squidcloud-backend`
8
+
9
+ Published to PyPI. Install with:
10
+ ```bash
11
+ pip install squidcloud-backend
12
+ ```
13
+
14
+ Depends on `squidcloud-client` (the Python client SDK) for `self.squid` access.
15
+
16
+ ## File Structure
17
+
18
+ ```
19
+ squidcloud_backend/
20
+ ├── __init__.py # Public API exports
21
+ ├── types.py # All TypedDict types, enums, type aliases
22
+ ├── context.py # Request-scoped context (contextvars — Python's AsyncLocalStorage)
23
+ ├── metadata.py # Meta singleton — collects decorator registrations
24
+ ├── decorators.py # All 27 decorators (@executable, @trigger, etc.)
25
+ ├── service.py # SquidService base class
26
+ ├── project.py # SquidProject entry point
27
+ └── llm_service.py # SquidLlmService abstract base
28
+ ```
29
+
30
+ ## How It Works
31
+
32
+ ### Decorator Registration
33
+
34
+ Python decorators execute before the class body is complete, so we use a
35
+ deferred registration pattern:
36
+
37
+ 1. Each decorator stores a `(name, register_fn)` tuple on the function via
38
+ the `__squid_decorators__` attribute
39
+ 2. When the class is defined, `SquidService.__init_subclass__()` iterates all
40
+ methods and calls each `register_fn(cls, method_name)`
41
+ 3. `register_fn` calls the corresponding method on the `Meta` singleton
42
+ 4. Class-level decorators (`@llm_service`, `@mcp_server`) store options as
43
+ class attributes, processed in `__init_subclass__()`
44
+
45
+ ### Request Context (contextvars)
46
+
47
+ The Python equivalent of Node.js `AsyncLocalStorage` is `contextvars.ContextVar`.
48
+ Per-request state (auth, RunContext) is stored in a context var and accessed
49
+ via `SquidService.context`, `SquidService.get_user_auth()`, etc.
50
+
51
+ ```python
52
+ # The worker sets the context before calling a service method:
53
+ with RequestScope(ServiceRequestConfig(auth=auth, context=run_context)):
54
+ result = service.my_method(*params)
55
+
56
+ # Inside the method, the service accesses it:
57
+ class MyService(SquidService):
58
+ @executable()
59
+ async def my_method(self) -> str:
60
+ user = self.get_user_auth() # reads from context var
61
+ return f"Hello, {user['userId']}!"
62
+ ```
63
+
64
+ ### Environment Variables
65
+
66
+ The following environment variables are used:
67
+
68
+ | Variable | Description | Default |
69
+ |----------|-------------|---------|
70
+ | `SQUID_REGION` | Deployment region (e.g., `us-east-1.aws`) | `local` |
71
+ | `SQUID_APP_ID` | Application ID | — |
72
+
73
+ Secrets and API keys are NOT set via env vars — they are passed per-request
74
+ in the `ExecuteFunctionPayload` from the tenant/local-backend.
75
+
76
+ ### Global State
77
+
78
+ The worker subprocess sets these class-level globals on `SquidService` before
79
+ each request:
80
+
81
+ - `_global_secrets` — Custom secrets from the Squid console
82
+ - `_global_api_keys` — API keys from the Squid console
83
+ - `_global_backend_api_key` — Internal backend API key
84
+ - `_global_app_id` — Application ID
85
+ - `_global_assets_directory` — Path to the `public/` assets directory
86
+
87
+ ### self.squid — Accessing the Client SDK
88
+
89
+ `SquidService.squid` returns a `squidcloud.Squid` instance (from the
90
+ `squidcloud-client` package). It is initialized lazily using the backend API
91
+ key and region. Backend services can call any Squid API:
92
+
93
+ ```python
94
+ class MyService(SquidService):
95
+ @executable()
96
+ async def summarize(self, url: str) -> str:
97
+ content = await self.squid.web().get_url_content(url)
98
+ return await self.squid.ai().agent("summarizer").ask(content)
99
+ ```
100
+
101
+ ## Types
102
+
103
+ All types use `TypedDict` for JSON compatibility and IDE autocompletion.
104
+ Key types:
105
+
106
+ - **Request/Response**: `WebhookRequest`, `WebhookResponse`, `TriggerRequest`,
107
+ `OpenApiResponse`, `SquidFile`
108
+ - **Auth**: `AuthWithBearer`, `AuthWithApiKey`, `RunContext`, `ServiceRequestConfig`
109
+ - **Decorator options**: `TriggerOptions`, `SchedulerOptions`, `AiFunctionParam`,
110
+ `LimiterConfig`, `McpServerOptions`, `LlmServiceOptions`
111
+ - **Enums**: `CronExpression` (50+ common cron schedules)
112
+ - **Action types**: `DatabaseActionType`, `StorageActionType`, `TopicActionType`,
113
+ `MetricActionType`, `MutationType`
114
+
115
+ ## Metadata Output
116
+
117
+ `SquidProject.metadata()` returns a dict matching the `ApplicationBundleData`
118
+ JSON schema. This is consumed by the platform to discover and route:
119
+ executables, webhooks, triggers, schedulers, security rules, AI functions,
120
+ LLM services, MCP servers, and rate/quota limits.
121
+
122
+ ## Compatibility with TypeScript SDK
123
+
124
+ This SDK mirrors the TypeScript `@squidcloud/backend` package:
125
+
126
+ | TypeScript | Python |
127
+ |------------|--------|
128
+ | `class MyService extends SquidService` | `class MyService(SquidService)` |
129
+ | `@executable()` | `@executable()` |
130
+ | `@trigger('id', 'collection')` | `@trigger('id', 'collection')` |
131
+ | `@scheduler('id', CronExpression.EVERY_HOUR)` | `@scheduler('id', CronExpression.EVERY_HOUR)` |
132
+ | `@webhook('id')` | `@webhook('id')` |
133
+ | `@aiFunction('desc', params)` | `@ai_function('desc', params)` |
134
+ | `this.squid` | `self.squid` |
135
+ | `this.context` | `self.context` |
136
+ | `this.secrets` | `self.secrets` |
137
+ | `this.createWebhookResponse(...)` | `self.create_webhook_response(...)` |
138
+ | `new SquidProject()` | `SquidProject()` |
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "squidcloud-backend"
7
+ version = "1.0.0"
8
+ description = "Squid Cloud Python backend SDK — decorators, services, and project entry point"
9
+ requires-python = ">=3.11"
10
+ license = "MIT"
11
+ authors = [{name = "Squid Cloud", email = "support@squid.cloud"}]
12
+ classifiers = [
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.11",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Operating System :: OS Independent",
18
+ "Typing :: Typed",
19
+ ]
20
+ dependencies = [
21
+ "squidcloud-client>=1.0.0",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://squid.cloud"
26
+ Documentation = "https://docs.squid.cloud"
27
+ Repository = "https://github.com/squid-cloud/squid-cloud"
28
+
29
+ [project.optional-dependencies]
30
+ dev = [
31
+ "pytest>=7.0",
32
+ "pytest-asyncio>=0.21",
33
+ "ruff>=0.11.0",
34
+ ]
35
+
36
+ [tool.setuptools.packages.find]
37
+ include = ["squidcloud_backend*"]
38
+
39
+ [tool.ruff]
40
+ target-version = "py311"
41
+ line-length = 100
42
+
43
+ [tool.ruff.lint]
44
+ select = ["E", "W", "F", "I", "UP", "B", "SIM", "TCH", "RUF"]
45
+ ignore = ["E501", "UP007"]
46
+
47
+ [tool.ruff.lint.isort]
48
+ known-first-party = ["squidcloud_backend"]
49
+
50
+ [tool.ruff.format]
51
+ quote-style = "double"
52
+ indent-style = "space"
53
+ docstring-code-format = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,155 @@
1
+ """Squid Cloud Python backend SDK.
2
+
3
+ Provides decorators, base classes, and types for building Squid backend
4
+ services in Python.
5
+
6
+ Usage::
7
+
8
+ from squidcloud_backend import SquidService, SquidProject, executable, webhook
9
+
10
+ class MyService(SquidService):
11
+ @webhook("hello")
12
+ async def hello_webhook(self, request: WebhookRequest) -> WebhookResponse:
13
+ return self.create_webhook_response(body={"message": "Hello!"})
14
+
15
+ @executable()
16
+ async def greet(self, name: str) -> str:
17
+ return f"Hello, {name}!"
18
+
19
+ project = SquidProject()
20
+ """
21
+
22
+ from squidcloud_backend.decorators import (
23
+ ai_function,
24
+ ai_functions_configurator,
25
+ client_connection_state_handler,
26
+ executable,
27
+ limits,
28
+ llm_ask,
29
+ llm_service,
30
+ mcp_authorizer,
31
+ mcp_server,
32
+ mcp_tool,
33
+ scheduler,
34
+ secure_ai_agent,
35
+ secure_ai_query,
36
+ secure_api,
37
+ secure_collection,
38
+ secure_database,
39
+ secure_distributed_lock,
40
+ secure_graphql,
41
+ secure_metric,
42
+ secure_native_query,
43
+ secure_storage,
44
+ secure_topic,
45
+ transform_collection,
46
+ transform_database,
47
+ trigger,
48
+ webhook,
49
+ )
50
+ from squidcloud_backend.llm_service import SquidLlmService
51
+ from squidcloud_backend.project import SquidProject
52
+ from squidcloud_backend.service import (
53
+ OpenApiResponseException,
54
+ SquidService,
55
+ WebhookResponseException,
56
+ )
57
+ from squidcloud_backend.types import (
58
+ AiFunctionAttributes,
59
+ AiFunctionMetadataOptions,
60
+ AiFunctionParam,
61
+ AiFunctionsConfiguratorMetadataOptions,
62
+ Auth,
63
+ AuthWithApiKey,
64
+ AuthWithBearer,
65
+ ClientConnectionState,
66
+ CronExpression,
67
+ LimiterConfig,
68
+ LlmServiceOptions,
69
+ McpAuthorizationRequest,
70
+ McpServerOptions,
71
+ McpToolMetadataOptions,
72
+ OnIntegrationLifecycleInput,
73
+ OpenApiResponse,
74
+ QuotaLimitOptions,
75
+ RateLimitOptions,
76
+ RunContext,
77
+ SchedulerOptions,
78
+ SecureMetricOptions,
79
+ ServiceRequestConfig,
80
+ SquidFile,
81
+ TriggerOptions,
82
+ TriggerRequest,
83
+ UserAiAskResponse,
84
+ UserAiChatOptions,
85
+ WebhookRequest,
86
+ WebhookResponse,
87
+ )
88
+
89
+ __all__ = [
90
+ # Core classes
91
+ "SquidService",
92
+ "SquidProject",
93
+ "SquidLlmService",
94
+ # Exceptions
95
+ "WebhookResponseException",
96
+ "OpenApiResponseException",
97
+ # Decorators
98
+ "ai_function",
99
+ "ai_functions_configurator",
100
+ "client_connection_state_handler",
101
+ "executable",
102
+ "limits",
103
+ "llm_ask",
104
+ "llm_service",
105
+ "mcp_authorizer",
106
+ "mcp_server",
107
+ "mcp_tool",
108
+ "scheduler",
109
+ "secure_ai_agent",
110
+ "secure_ai_query",
111
+ "secure_api",
112
+ "secure_collection",
113
+ "secure_database",
114
+ "secure_distributed_lock",
115
+ "secure_graphql",
116
+ "secure_metric",
117
+ "secure_native_query",
118
+ "secure_storage",
119
+ "secure_topic",
120
+ "transform_collection",
121
+ "transform_database",
122
+ "trigger",
123
+ "webhook",
124
+ # Auth & Context
125
+ "Auth",
126
+ "AuthWithApiKey",
127
+ "AuthWithBearer",
128
+ "ClientConnectionState",
129
+ "RunContext",
130
+ "ServiceRequestConfig",
131
+ # Types
132
+ "AiFunctionAttributes",
133
+ "AiFunctionMetadataOptions",
134
+ "AiFunctionParam",
135
+ "AiFunctionsConfiguratorMetadataOptions",
136
+ "CronExpression",
137
+ "LimiterConfig",
138
+ "LlmServiceOptions",
139
+ "McpAuthorizationRequest",
140
+ "McpServerOptions",
141
+ "McpToolMetadataOptions",
142
+ "OnIntegrationLifecycleInput",
143
+ "OpenApiResponse",
144
+ "QuotaLimitOptions",
145
+ "RateLimitOptions",
146
+ "SchedulerOptions",
147
+ "SecureMetricOptions",
148
+ "SquidFile",
149
+ "TriggerOptions",
150
+ "TriggerRequest",
151
+ "UserAiAskResponse",
152
+ "UserAiChatOptions",
153
+ "WebhookRequest",
154
+ "WebhookResponse",
155
+ ]
@@ -0,0 +1,64 @@
1
+ """Request-scoped context management using Python contextvars.
2
+
3
+ Python equivalent of Node.js AsyncLocalStorage used in the TS SDK for
4
+ per-request state (auth, RunContext, etc.).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import contextvars
10
+ from typing import Any
11
+
12
+ from squidcloud_backend.types import ServiceRequestConfig
13
+
14
+ _current_request: contextvars.ContextVar[ServiceRequestConfig | None] = contextvars.ContextVar(
15
+ "_current_request", default=None
16
+ )
17
+
18
+
19
+ def get_request_config() -> ServiceRequestConfig:
20
+ """Get the current request's ServiceRequestConfig.
21
+
22
+ Raises:
23
+ RuntimeError: If called outside of a request scope.
24
+ """
25
+ config = _current_request.get()
26
+ if config is None:
27
+ raise RuntimeError(
28
+ "Failed to access request data outside of the request's scope. "
29
+ "This method can only be called during request execution."
30
+ )
31
+ return config
32
+
33
+
34
+ def set_request_config(config: ServiceRequestConfig) -> contextvars.Token:
35
+ """Set the request config for the current context. Returns a token for reset."""
36
+ return _current_request.set(config)
37
+
38
+
39
+ def reset_request_config(token: contextvars.Token) -> None:
40
+ """Reset the request config to its previous value."""
41
+ _current_request.reset(token)
42
+
43
+
44
+ class RequestScope:
45
+ """Context manager for running code within a request scope.
46
+
47
+ Usage::
48
+
49
+ with RequestScope(config):
50
+ result = service.my_method(*args)
51
+ """
52
+
53
+ def __init__(self, config: ServiceRequestConfig) -> None:
54
+ self._config = config
55
+ self._token: contextvars.Token | None = None
56
+
57
+ def __enter__(self) -> ServiceRequestConfig:
58
+ self._token = set_request_config(self._config)
59
+ return self._config
60
+
61
+ def __exit__(self, *args: Any) -> None:
62
+ if self._token is not None:
63
+ reset_request_config(self._token)
64
+ self._token = None