-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathmodels.py
More file actions
292 lines (223 loc) · 10.1 KB
/
models.py
File metadata and controls
292 lines (223 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
from enum import StrEnum
from typing import Self
from pydantic import BaseModel, Field, field_validator, model_validator
from utilities.time import iso_string
from utilities.validation import validate_any_fields_defined
class HostedMcpServerStatus(StrEnum):
"""Defines possible MCP server deployment states."""
CREATING = "Creating"
IN_SERVICE = "InService"
STARTING = "Starting"
STOPPING = "Stopping"
STOPPED = "Stopped"
UPDATING = "Updating"
DELETING = "Deleting"
FAILED = "Failed"
class McpServerStatus(StrEnum):
"""Enum representing the prompt template type."""
ACTIVE = "active"
INACTIVE = "inactive"
class McpServerModel(BaseModel):
"""
A Pydantic model representing a template for prompts.
Contains metadata and functionality to create new revisions.
"""
# Unique identifier for the mcp server
id: str | None = Field(default_factory=lambda: str(uuid.uuid4()))
# Timestamp of when the mcp server was created
created: str | None = Field(default_factory=iso_string)
# Owner of the MCP user
owner: str
# URL of the MCP server
url: str
# Name of the MCP server
name: str
# Description of the MCP server
description: str | None = Field(default_factory=lambda: None)
# Custom headers for the MCP client
customHeaders: dict | None = Field(default_factory=lambda: None)
# Custom client properties for the MCP client
clientConfig: dict | None = Field(default_factory=lambda: None)
# Status of the server set by admins
status: McpServerStatus | None = Field(default=McpServerStatus.ACTIVE)
# Groups of the MCP server
groups: list[str] | None = Field(default_factory=lambda: None)
class LoadBalancerHealthCheckConfig(BaseModel):
"""Specifies health check parameters for load balancer configuration."""
path: str = Field(min_length=1)
interval: int = Field(gt=0)
timeout: int = Field(gt=0)
healthyThresholdCount: int = Field(gt=0)
unhealthyThresholdCount: int = Field(gt=0)
class LoadBalancerConfig(BaseModel):
"""Defines load balancer settings."""
healthCheckConfig: LoadBalancerHealthCheckConfig
class ContainerHealthCheckConfig(BaseModel):
"""Specifies container health check parameters."""
command: str | list[str]
interval: int = Field(gt=0)
startPeriod: int = Field(ge=0)
timeout: int = Field(gt=0)
retries: int = Field(gt=0)
class AutoScalingConfig(BaseModel):
"""Auto-scaling configuration for hosted MCP servers."""
minCapacity: int
maxCapacity: int
targetValue: int | None = Field(default=None)
metricName: str | None = Field(default=None)
duration: int | None = Field(default=None)
cooldown: int | None = Field(default=None)
class AutoScalingConfigUpdate(BaseModel):
"""Updatable auto-scaling configuration for hosted MCP servers (all fields optional)."""
minCapacity: int | None = Field(default=None)
maxCapacity: int | None = Field(default=None)
targetValue: int | None = Field(default=None)
metricName: str | None = Field(default=None)
duration: int | None = Field(default=None)
cooldown: int | None = Field(default=None)
class HostedMcpServerModel(BaseModel):
"""
A Pydantic model representing a hosted MCP server configuration.
This model is used for creating MCP servers that are deployed on ECS Fargate.
"""
# Unique identifier for the mcp server
id: str | None = Field(default_factory=lambda: str(uuid.uuid4()))
# Timestamp of when the mcp server was created
created: str | None = Field(default_factory=iso_string)
# Owner of the MCP server
owner: str
# Name of the MCP server
name: str
# Description of the MCP server
description: str | None = Field(default_factory=lambda: None)
# Command to start the server
startCommand: str
# Port number (optional, used for HTTP/SSE servers)
port: int | None = Field(default=None)
# Server type: 'stdio', 'http', or 'sse'
serverType: str
# Container image (optional)
# If provided without s3Path: use as pre-built container image
# If provided with s3Path: use as base image for building from S3 artifacts
image: str | None = Field(default=None)
# S3 path to server artifacts (binaries, Python files, etc.)
# If provided with image: image is used as base image for building
# If provided without image: default base image is used
s3Path: str | None = Field(default=None)
# Auto-scaling configuration
autoScalingConfig: AutoScalingConfig
# Load balancer configuration (optional, will use defaults if not provided)
loadBalancerConfig: LoadBalancerConfig | None = Field(default=None)
# Container health check configuration (optional, will use defaults if not provided)
containerHealthCheckConfig: ContainerHealthCheckConfig | None = Field(default=None)
# Environment variables for the container
environment: dict[str, str] | None = Field(default_factory=lambda: None)
# IAM role ARN for task execution (optional, will be auto-created if not provided)
taskExecutionRoleArn: str | None = Field(default=None)
# IAM role ARN for running tasks (optional, will be auto-created if not provided)
taskRoleArn: str | None = Field(default=None)
# Fargate CPU units (defaults to 256 which equals 0.25 vCPU)
cpu: int | None = Field(default=256)
# Fargate memory limit in MiB (defaults to 512 MiB)
memoryLimitMiB: int | None = Field(default=512)
# Groups of the MCP server (for authorization)
groups: list[str] | None = Field(default_factory=lambda: None)
# Status of the server
status: HostedMcpServerStatus | None = Field(default=HostedMcpServerStatus.CREATING)
class UpdateHostedMcpServerRequest(BaseModel):
"""Specifies parameters for hosted MCP server update requests."""
enabled: bool | None = None
autoScalingConfig: AutoScalingConfigUpdate | None = None
environment: dict[str, str] | None = None
containerHealthCheckConfig: ContainerHealthCheckConfig | None = None
loadBalancerConfig: LoadBalancerConfig | None = None
cpu: int | None = None
memoryLimitMiB: int | None = None
description: str | None = None
groups: list[str] | None = None
@model_validator(mode="after")
def validate_update_request(self) -> Self:
"""Validates update request parameters."""
fields = [
self.enabled,
self.autoScalingConfig,
self.environment,
self.containerHealthCheckConfig,
self.loadBalancerConfig,
self.cpu,
self.memoryLimitMiB,
self.description,
self.groups,
]
if not validate_any_fields_defined(fields):
raise ValueError(
"At least one field out of enabled, autoScalingConfig, environment, "
"containerHealthCheckConfig, loadBalancerConfig, cpu, memoryLimitMiB, "
"description, or groups must be defined in request payload."
)
return self
@field_validator("autoScalingConfig")
@classmethod
def validate_autoscaling_config(cls, config: AutoScalingConfig | None) -> AutoScalingConfig | None:
"""Validates auto-scaling configuration."""
if config is not None and not config:
raise ValueError("The autoScalingConfig must not be null if defined in request payload.")
return config
@field_validator("containerHealthCheckConfig")
@classmethod
def validate_container_health_check_config(
cls, config: ContainerHealthCheckConfig | None
) -> ContainerHealthCheckConfig | None:
"""Validates container health check configuration."""
if config is not None and not config:
raise ValueError("The containerHealthCheckConfig must not be null if defined in request payload.")
return config
@field_validator("loadBalancerConfig")
@classmethod
def validate_load_balancer_config(cls, config: LoadBalancerConfig | None) -> LoadBalancerConfig | None:
"""Validates load balancer configuration."""
if config is not None and not config:
raise ValueError("The loadBalancerConfig must not be null if defined in request payload.")
return config
@field_validator("cpu")
@classmethod
def validate_cpu(cls, cpu: int | None) -> int | None:
"""Validates CPU units."""
if cpu is not None:
# Fargate CPU must be in valid units: 256, 512, 1024, 2048, 4096
valid_cpu_values = [256, 512, 1024, 2048, 4096]
if cpu not in valid_cpu_values:
raise ValueError(f"CPU must be one of {valid_cpu_values}")
return cpu
@field_validator("memoryLimitMiB")
@classmethod
def validate_memory(cls, memory: int | None) -> int | None:
"""Validates memory limit."""
if memory is not None:
if memory < 512:
raise ValueError("Memory limit must be at least 512 MiB")
if memory > 30720:
raise ValueError("Memory limit must be at most 30720 MiB")
return memory
class BedrockAgentApprovalPut(BaseModel):
"""Admin catalog entry for a Bedrock agent (DynamoDB)."""
agentAliasId: str = Field(min_length=1, description="Alias ID used for InvokeAgent")
agentName: str = Field(min_length=1, description="Display name snapshot")
groups: list[str] | None = Field(
default=None,
description="group:... tokens; omit or empty = all authenticated users",
)