Add model provider configuration writes to the app-server (#58876)

## What changed

Add `modelProvider/config/write` to create custom gateways and patch existing custom gateway, Bedrock, and GCP settings. Return the saved provider using the same safe summary as `modelProvider/list`.

Omitted fields retain their values; explicit `null` clears nullable user overrides. Apply patches through the shared configuration queue with last-write-wins semantics, enforce field editability and validation, and preserve unexposed authentication settings.

Saving leaves provider selection and authentication unchanged. Saving the provider used by `model/list` invalidates its catalog for the next access. Include documentation and generated JSON, TypeScript, and Python protocol types.

## Testing

Add coverage for gateway creation and ID allocation, patch and null semantics, configuration policy enforcement, authentication preservation, and catalog refresh after active-provider edits. Extend Python protocol tests to verify wire round trips and reject mismatched provider IDs or fields.

GitOrigin-RevId: 850fe2c48ef5274944400efb5a859d720179c923
This commit is contained in:
alexsong-oai
2026-11-02 12:22:22 +00:00
committed by copyberry
parent 3026a8e54e
commit 053e5b1ca9
33 changed files with 2357 additions and 92 deletions
@@ -426,6 +426,11 @@ class AutoReviewRequirements(BaseModel):
required_on_models: Annotated[list[str] | None, Field(alias="requiredOnModels")] = None
class BedrockProviderId(Enum):
amazon_bedrock = "amazon-bedrock"
amazon_bedrock_runtime = "amazon-bedrock-runtime"
class BrowserUseAccessApprovalLifetime(Enum):
turn = "turn"
thread = "thread"
@@ -1850,6 +1855,10 @@ class GatewayOAuthStatus(Enum):
failed = "failed"
class GcpProviderId(RootModel[Literal["gcp"]]):
root: Literal["gcp"]
class GetAccountParams(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -3779,6 +3788,62 @@ class ProviderSettings(
)
class CustomGatewayProviderSettingsPatch(BaseModel):
model_config = ConfigDict(
extra="forbid",
populate_by_name=True,
)
base_url: Annotated[str | None, Field(alias="baseUrl")] = None
env_key: Annotated[str | None, Field(alias="envKey")] = None
name: str | None = None
provider_id: Annotated[
str | None,
Field(
alias="providerId",
description="Omit or pass null to allocate a new custom provider ID.",
),
] = None
type: Annotated[Literal["customGateway"], Field(title="CustomGatewayProviderSettingsPatchType")]
class BedrockProviderSettingsPatch(BaseModel):
model_config = ConfigDict(
extra="forbid",
populate_by_name=True,
)
profile: str | None = None
provider_id: Annotated[BedrockProviderId, Field(alias="providerId")]
region: str | None = None
type: Annotated[Literal["bedrock"], Field(title="BedrockProviderSettingsPatchType")]
class GcpProviderSettingsPatch(BaseModel):
model_config = ConfigDict(
extra="forbid",
populate_by_name=True,
)
credential_file: Annotated[str | None, Field(alias="credentialFile")] = None
location: str | None = None
project_id: Annotated[str | None, Field(alias="projectId")] = None
provider_id: Annotated[GcpProviderId, Field(alias="providerId")]
type: Annotated[Literal["gcp"], Field(title="GcpProviderSettingsPatchType")]
class ProviderSettingsPatch(
RootModel[
CustomGatewayProviderSettingsPatch | BedrockProviderSettingsPatch | GcpProviderSettingsPatch
]
):
root: Annotated[
CustomGatewayProviderSettingsPatch
| BedrockProviderSettingsPatch
| GcpProviderSettingsPatch,
Field(
description="Omitted patch fields preserve their values. Explicit null clears a nullable field."
),
]
class RateLimitReachedType(Enum):
rate_limit_reached = "rate_limit_reached"
workspace_owner_credits_depleted = "workspace_owner_credits_depleted"
@@ -9336,6 +9401,14 @@ class ModelListResponse(BaseModel):
] = None
class ModelProviderConfigWriteParams(BaseModel):
model_config = ConfigDict(
extra="forbid",
populate_by_name=True,
)
settings: ProviderSettingsPatch
class ModelProviderSummary(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -11409,6 +11482,18 @@ class ReviewStartRequest(BaseModel):
params: ReviewStartParams
class ModelProviderConfigWriteRequest(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
id: RequestId
method: Annotated[
Literal["modelProvider/config/write"],
Field(title="ModelProvider/config/writeRequestMethod"),
]
params: ModelProviderConfigWriteParams
class McpServerStatusListRequest(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -11774,6 +11859,13 @@ class ListMcpServerStatusResponse(BaseModel):
] = None
class ModelProviderConfigWriteResponse(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
provider: ModelProviderSummary
class ModelProviderListResponse(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -13489,6 +13581,7 @@ class ClientRequest(
| AccountGatewayOAuthLoginRequest
| AccountGatewayOAuthCancelRequest
| ModelProviderListRequest
| ModelProviderConfigWriteRequest
| ModelProviderCapabilitiesReadRequest
| ExperimentalFeatureListRequest
| PermissionProfileListRequest
@@ -13599,6 +13692,7 @@ class ClientRequest(
| AccountGatewayOAuthLoginRequest
| AccountGatewayOAuthCancelRequest
| ModelProviderListRequest
| ModelProviderConfigWriteRequest
| ModelProviderCapabilitiesReadRequest
| ExperimentalFeatureListRequest
| PermissionProfileListRequest