diff --git a/.nextchanges/bundles/ai-gateway-model-provider-service.md b/.nextchanges/bundles/ai-gateway-model-provider-service.md new file mode 100644 index 00000000000..349334fc407 --- /dev/null +++ b/.nextchanges/bundles/ai-gateway-model-provider-service.md @@ -0,0 +1 @@ +* Add bundle support for the AI Gateway `model_provider_service` resource (direct engine). ([#6634](https://github.com/databricks/cli/pull/6634)) diff --git a/acceptance/bundle/deployment/bind/model_provider_service/databricks.yml b/acceptance/bundle/deployment/bind/model_provider_service/databricks.yml new file mode 100644 index 00000000000..18717f5715e --- /dev/null +++ b/acceptance/bundle/deployment/bind/model_provider_service/databricks.yml @@ -0,0 +1,16 @@ +bundle: + name: test-bundle + +resources: + model_provider_services: + mps1: + parent: schemas/main.myschema + model_provider_service_id: mysvc + comment: bound service + config: + provider_type: EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM + custom: + direct: + base_url: https://api.example.com/v1 + api_key: + plaintext: dummy-api-key diff --git a/acceptance/bundle/deployment/bind/model_provider_service/out.test.toml b/acceptance/bundle/deployment/bind/model_provider_service/out.test.toml new file mode 100644 index 00000000000..27ec2a7fcd6 --- /dev/null +++ b/acceptance/bundle/deployment/bind/model_provider_service/out.test.toml @@ -0,0 +1,3 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.DMS = [""] diff --git a/acceptance/bundle/deployment/bind/model_provider_service/output.txt b/acceptance/bundle/deployment/bind/model_provider_service/output.txt new file mode 100644 index 00000000000..cafdcf3d28b --- /dev/null +++ b/acceptance/bundle/deployment/bind/model_provider_service/output.txt @@ -0,0 +1,30 @@ + +>>> [CLI] bundle deployment bind mps1 main.myschema.mysvc --auto-approve +Successfully bound model_provider_service with an id 'main.myschema.mysvc' +Run 'bundle deploy' to deploy changes to your workspace + +>>> [CLI] bundle summary +Name: test-bundle +Target: default +Workspace: + User: [USERNAME] + Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default +Resources: + Model provider services: + mps1: + Name: mysvc + URL: [DATABRICKS_URL]/explore/data/model-provider-services/main/myschema/mysvc?w=[NUMID] + +>>> [CLI] bundle deployment unbind mps1 + +>>> [CLI] bundle summary +Name: test-bundle +Target: default +Workspace: + User: [USERNAME] + Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default +Resources: + Model provider services: + mps1: + Name: mysvc + URL: (not deployed) diff --git a/acceptance/bundle/deployment/bind/model_provider_service/script b/acceptance/bundle/deployment/bind/model_provider_service/script new file mode 100644 index 00000000000..c003c48256c --- /dev/null +++ b/acceptance/bundle/deployment/bind/model_provider_service/script @@ -0,0 +1,5 @@ +trace $CLI bundle deployment bind mps1 main.myschema.mysvc --auto-approve +trace $CLI bundle summary + +trace $CLI bundle deployment unbind mps1 +trace $CLI bundle summary diff --git a/acceptance/bundle/deployment/bind/model_provider_service/test.toml b/acceptance/bundle/deployment/bind/model_provider_service/test.toml new file mode 100644 index 00000000000..651520efa8f --- /dev/null +++ b/acceptance/bundle/deployment/bind/model_provider_service/test.toml @@ -0,0 +1,18 @@ +# AI Gateway securables are direct-engine only. +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] + +Cloud = false + +Ignore = [ + ".databricks", +] + +# The bind flow issues a GET to confirm the remote resource exists before binding. +[[Server]] +Pattern = "GET /api/2.1/unity-catalog/model-provider-services/{name}" +Response.Body = ''' +{ + "name": "model-provider-services/main.myschema.mysvc", + "comment": "bound service" +} +''' diff --git a/acceptance/bundle/invariant/configs/model_provider_service.yml.tmpl b/acceptance/bundle/invariant/configs/model_provider_service.yml.tmpl new file mode 100644 index 00000000000..2c7b0910021 --- /dev/null +++ b/acceptance/bundle/invariant/configs/model_provider_service.yml.tmpl @@ -0,0 +1,20 @@ +bundle: + name: test-bundle-$UNIQUE_NAME + +resources: + model_provider_services: + foo: + parent: schemas/main.default + model_provider_service_id: test-model-provider-service-$UNIQUE_NAME + comment: test model provider service + config: + provider_type: EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM + targets: + - model: gpt-4o + native_api_types: + - openai/v1/chat/completions + custom: + direct: + base_url: https://api.example.com/v1 + api_key: + plaintext: dummy-api-key diff --git a/acceptance/bundle/python/model_provider_services-support/databricks.yml b/acceptance/bundle/python/model_provider_services-support/databricks.yml new file mode 100644 index 00000000000..ef383951e01 --- /dev/null +++ b/acceptance/bundle/python/model_provider_services-support/databricks.yml @@ -0,0 +1,17 @@ +bundle: + name: my_project + +sync: {paths: []} # don't need to copy files + +python: + resources: + - "resources:load_resources" + mutators: + - "mutators:update_model_provider_service" + +resources: + model_provider_services: + my_model_provider_service_1: + parent: "schemas/main.default" + model_provider_service_id: "my_model_provider_service_1" + comment: "My model provider service" diff --git a/acceptance/bundle/python/model_provider_services-support/mutators.py b/acceptance/bundle/python/model_provider_services-support/mutators.py new file mode 100644 index 00000000000..03b76e35a46 --- /dev/null +++ b/acceptance/bundle/python/model_provider_services-support/mutators.py @@ -0,0 +1,16 @@ +from dataclasses import replace + +from databricks.bundles.model_provider_services import ModelProviderService +from databricks.bundles.core import model_provider_service_mutator + + +@model_provider_service_mutator +def update_model_provider_service( + model_provider_service: ModelProviderService, +) -> ModelProviderService: + assert isinstance(model_provider_service.comment, str) + + return replace( + model_provider_service, + comment=f"{model_provider_service.comment} (updated)", + ) diff --git a/acceptance/bundle/python/model_provider_services-support/out.test.toml b/acceptance/bundle/python/model_provider_services-support/out.test.toml new file mode 100644 index 00000000000..02da5baab89 --- /dev/null +++ b/acceptance/bundle/python/model_provider_services-support/out.test.toml @@ -0,0 +1,4 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] +EnvMatrix.DMS = ["", "true"] +EnvMatrix.PYDAB_VERSION = ["current"] diff --git a/acceptance/bundle/python/model_provider_services-support/output.txt b/acceptance/bundle/python/model_provider_services-support/output.txt new file mode 100644 index 00000000000..b3f971b560a --- /dev/null +++ b/acceptance/bundle/python/model_provider_services-support/output.txt @@ -0,0 +1,28 @@ + +>>> uv run [UV_ARGS] -q [CLI] bundle validate --output json +{ + "experimental": { + "python": { + "mutators": [ + "mutators:update_model_provider_service" + ], + "resources": [ + "resources:load_resources" + ] + } + }, + "resources": { + "model_provider_services": { + "my_model_provider_service_1": { + "comment": "My model provider service (updated)", + "model_provider_service_id": "my_model_provider_service_1", + "parent": "schemas/main.default" + }, + "my_model_provider_service_2": { + "comment": "My model provider service (2) (updated)", + "model_provider_service_id": "my_model_provider_service_2", + "parent": "schemas/main.default" + } + } + } +} diff --git a/acceptance/bundle/python/model_provider_services-support/resources.py b/acceptance/bundle/python/model_provider_services-support/resources.py new file mode 100644 index 00000000000..5dceaf37b68 --- /dev/null +++ b/acceptance/bundle/python/model_provider_services-support/resources.py @@ -0,0 +1,16 @@ +from databricks.bundles.core import Resources + + +def load_resources() -> Resources: + resources = Resources() + + resources.add_model_provider_service( + "my_model_provider_service_2", + { + "parent": "schemas/main.default", + "model_provider_service_id": "my_model_provider_service_2", + "comment": "My model provider service (2)", + }, + ) + + return resources diff --git a/acceptance/bundle/python/model_provider_services-support/script b/acceptance/bundle/python/model_provider_services-support/script new file mode 100644 index 00000000000..e273fb45a53 --- /dev/null +++ b/acceptance/bundle/python/model_provider_services-support/script @@ -0,0 +1,5 @@ + +trace uv run $UV_ARGS -q $CLI bundle validate --output json | \ + jq "pick(.experimental.python, .resources)" + +rm -fr .databricks __pycache__ diff --git a/acceptance/bundle/python/model_provider_services-support/test.toml b/acceptance/bundle/python/model_provider_services-support/test.toml new file mode 100644 index 00000000000..b9ab1057685 --- /dev/null +++ b/acceptance/bundle/python/model_provider_services-support/test.toml @@ -0,0 +1,4 @@ +Cloud = false # tests don't interact with APIs + +# model_provider_services are only supported in the current version of the wheel +EnvMatrix.PYDAB_VERSION = ["current"] diff --git a/acceptance/bundle/refschema/out.fields.txt b/acceptance/bundle/refschema/out.fields.txt index 750445182bd..faadf3d8dbe 100644 --- a/acceptance/bundle/refschema/out.fields.txt +++ b/acceptance/bundle/refschema/out.fields.txt @@ -2103,6 +2103,92 @@ resources.mcp_services.*.mcp_service_id string ALL resources.mcp_services.*.modified_status string INPUT resources.mcp_services.*.parent string ALL resources.mcp_services.*.url string INPUT +resources.model_provider_services.*.comment string ALL +resources.model_provider_services.*.config *catalog.ModelProviderServiceConfig ALL +resources.model_provider_services.*.config.allow_all_targets bool ALL +resources.model_provider_services.*.config.amazon_bedrock *catalog.ModelProviderServiceConfigAmazonBedrockProviderConfig ALL +resources.model_provider_services.*.config.amazon_bedrock.direct *catalog.ModelProviderServiceConfigAmazonBedrockProviderDirectConfig ALL +resources.model_provider_services.*.config.amazon_bedrock.direct.aws_access_key *catalog.ModelProviderServiceConfigAwsAccessKey ALL +resources.model_provider_services.*.config.amazon_bedrock.direct.aws_access_key.access_key_id string ALL +resources.model_provider_services.*.config.amazon_bedrock.direct.aws_access_key.secret_access_key *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.amazon_bedrock.direct.aws_access_key.secret_access_key.plaintext string ALL +resources.model_provider_services.*.config.amazon_bedrock.direct.region string ALL +resources.model_provider_services.*.config.amazon_bedrock.direct.service_credential *catalog.ModelProviderServiceConfigServiceCredential ALL +resources.model_provider_services.*.config.amazon_bedrock.direct.service_credential.name string ALL +resources.model_provider_services.*.config.anthropic *catalog.ModelProviderServiceConfigAnthropicProviderConfig ALL +resources.model_provider_services.*.config.anthropic.direct *catalog.ModelProviderServiceConfigAnthropicProviderDirectConfig ALL +resources.model_provider_services.*.config.anthropic.direct.api_key *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.anthropic.direct.api_key.plaintext string ALL +resources.model_provider_services.*.config.anthropic.relayed *catalog.ModelProviderServiceConfigAnthropicProviderRelayedConfig ALL +resources.model_provider_services.*.config.azure_openai *catalog.ModelProviderServiceConfigAzureOpenAiProviderConfig ALL +resources.model_provider_services.*.config.azure_openai.direct *catalog.ModelProviderServiceConfigAzureOpenAiProviderDirectConfig ALL +resources.model_provider_services.*.config.azure_openai.direct.api_key *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.azure_openai.direct.api_key.plaintext string ALL +resources.model_provider_services.*.config.azure_openai.direct.base_url string ALL +resources.model_provider_services.*.config.azure_openai.direct.entra_service_principal *catalog.ModelProviderServiceConfigEntraServicePrincipal ALL +resources.model_provider_services.*.config.azure_openai.direct.entra_service_principal.client_id string ALL +resources.model_provider_services.*.config.azure_openai.direct.entra_service_principal.client_secret *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.azure_openai.direct.entra_service_principal.client_secret.plaintext string ALL +resources.model_provider_services.*.config.azure_openai.direct.entra_service_principal.tenant_id string ALL +resources.model_provider_services.*.config.azure_openai.direct.service_credential *catalog.ModelProviderServiceConfigServiceCredential ALL +resources.model_provider_services.*.config.azure_openai.direct.service_credential.name string ALL +resources.model_provider_services.*.config.custom *catalog.ModelProviderServiceConfigCustomProviderConfig ALL +resources.model_provider_services.*.config.custom.direct *catalog.ModelProviderServiceConfigCustomProviderDirectConfig ALL +resources.model_provider_services.*.config.custom.direct.api_key *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.custom.direct.api_key.plaintext string ALL +resources.model_provider_services.*.config.custom.direct.base_url string ALL +resources.model_provider_services.*.config.forward_headers bool ALL +resources.model_provider_services.*.config.forward_query_parameters bool ALL +resources.model_provider_services.*.config.forward_unmanaged_paths bool ALL +resources.model_provider_services.*.config.gemini_enterprise *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderConfig ALL +resources.model_provider_services.*.config.gemini_enterprise.direct *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig ALL +resources.model_provider_services.*.config.gemini_enterprise.direct.api_key *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.gemini_enterprise.direct.api_key.plaintext string ALL +resources.model_provider_services.*.config.gemini_enterprise.direct.project_id string ALL +resources.model_provider_services.*.config.gemini_enterprise.direct.region string ALL +resources.model_provider_services.*.config.inference_table *catalog.InferenceTableConfig ALL +resources.model_provider_services.*.config.inference_table.is_deleted bool ALL +resources.model_provider_services.*.config.inference_table.parent string ALL +resources.model_provider_services.*.config.inference_table.table string ALL +resources.model_provider_services.*.config.inference_table.table_name_prefix string ALL +resources.model_provider_services.*.config.microsoft_foundry *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderConfig ALL +resources.model_provider_services.*.config.microsoft_foundry.direct *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.api_key *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.api_key.plaintext string ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.base_url string ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.entra_service_principal *catalog.ModelProviderServiceConfigEntraServicePrincipal ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.entra_service_principal.client_id string ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.entra_service_principal.client_secret *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.entra_service_principal.client_secret.plaintext string ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.entra_service_principal.tenant_id string ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.service_credential *catalog.ModelProviderServiceConfigServiceCredential ALL +resources.model_provider_services.*.config.microsoft_foundry.direct.service_credential.name string ALL +resources.model_provider_services.*.config.openai *catalog.ModelProviderServiceConfigOpenAiProviderConfig ALL +resources.model_provider_services.*.config.openai.direct *catalog.ModelProviderServiceConfigOpenAiProviderDirectConfig ALL +resources.model_provider_services.*.config.openai.direct.api_key *catalog.ModelProviderServiceConfigProviderSecret ALL +resources.model_provider_services.*.config.openai.direct.api_key.plaintext string ALL +resources.model_provider_services.*.config.openai.direct.base_url string ALL +resources.model_provider_services.*.config.openai.direct.organization string ALL +resources.model_provider_services.*.config.provider_type catalog.ModelProviderServiceConfigExternalModelProviderType ALL +resources.model_provider_services.*.config.rate_limits []catalog.RateLimit ALL +resources.model_provider_services.*.config.rate_limits[*] catalog.RateLimit ALL +resources.model_provider_services.*.config.rate_limits[*].key catalog.RateLimitRateLimitKey ALL +resources.model_provider_services.*.config.rate_limits[*].principal string ALL +resources.model_provider_services.*.config.rate_limits[*].renewal_period catalog.RateLimitRateLimitRenewalPeriod ALL +resources.model_provider_services.*.config.rate_limits[*].requests int64 ALL +resources.model_provider_services.*.config.rate_limits[*].tokens int64 ALL +resources.model_provider_services.*.config.targets []catalog.ModelProviderServiceConfigModelTargetConfig ALL +resources.model_provider_services.*.config.targets[*] catalog.ModelProviderServiceConfigModelTargetConfig ALL +resources.model_provider_services.*.config.targets[*].model string ALL +resources.model_provider_services.*.config.targets[*].native_api_types []string ALL +resources.model_provider_services.*.config.targets[*].native_api_types[*] string ALL +resources.model_provider_services.*.id string INPUT +resources.model_provider_services.*.lifecycle resources.Lifecycle INPUT +resources.model_provider_services.*.lifecycle.prevent_destroy bool INPUT +resources.model_provider_services.*.model_provider_service_id string ALL +resources.model_provider_services.*.modified_status string INPUT +resources.model_provider_services.*.parent string ALL +resources.model_provider_services.*.url string INPUT resources.model_services.*.comment string ALL resources.model_services.*.config *catalog.ModelServiceConfig ALL resources.model_services.*.config.inference_table *catalog.InferenceTableConfig ALL diff --git a/acceptance/bundle/resources/model_provider_services/basic/databricks.yml b/acceptance/bundle/resources/model_provider_services/basic/databricks.yml new file mode 100644 index 00000000000..aeac81263d9 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/basic/databricks.yml @@ -0,0 +1,20 @@ +bundle: + name: test-bundle + +resources: + model_provider_services: + mps1: + parent: schemas/main.myschema + model_provider_service_id: myservice + comment: COMMENT1 + config: + provider_type: EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM + targets: + - model: gpt-4o + native_api_types: + - openai/v1/chat/completions + custom: + direct: + base_url: https://api.example.com/v1 + api_key: + plaintext: dummy-api-key diff --git a/acceptance/bundle/resources/model_provider_services/basic/out.test.toml b/acceptance/bundle/resources/model_provider_services/basic/out.test.toml new file mode 100644 index 00000000000..59b56a2037c --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/basic/out.test.toml @@ -0,0 +1,3 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.DMS = ["", "true"] diff --git a/acceptance/bundle/resources/model_provider_services/basic/output.txt b/acceptance/bundle/resources/model_provider_services/basic/output.txt new file mode 100644 index 00000000000..a3dfbff46fa --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/basic/output.txt @@ -0,0 +1,149 @@ + +=== Initial summary before deploy +>>> [CLI] bundle summary -o json +{ + "comment": "COMMENT1", + "config": { + "custom": { + "direct": { + "api_key": { + "plaintext": "dummy-api-key" + }, + "base_url": "https://api.example.com/v1" + } + }, + "provider_type": "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", + "targets": [ + { + "model": "gpt-4o", + "native_api_types": [ + "openai/v1/chat/completions" + ] + } + ] + }, + "model_provider_service_id": "myservice", + "modified_status": "created", + "parent": "schemas/main.myschema" +} + +=== Verify it does not exist yet +>>> musterr [CLI] ai-gateway get-model-provider-service model-provider-services/main.myschema.myservice +Error: Resource catalog.ModelProviderService not found: main.myschema.myservice + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Created model_provider_services.mps1 +Files: 3 uploaded, 0 deleted +Resources: 1 created, 0 changed, 0 deleted, 0 unchanged + +>>> print_requests.py //unity +{ + "method": "POST", + "path": "/api/2.1/unity-catalog/model-provider-services", + "q": { + "model_provider_service_id": "myservice", + "parent": "schemas/main.myschema" + }, + "body": { + "comment": "COMMENT1", + "config": { + "custom": { + "direct": { + "api_key": { + "plaintext": "dummy-api-key" + }, + "base_url": "https://api.example.com/v1" + } + }, + "provider_type": "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", + "targets": [ + { + "model": "gpt-4o", + "native_api_types": [ + "openai/v1/chat/completions" + ] + } + ] + } + } +} + +=== Summary should show the id and the Catalog Explorer url +>>> [CLI] bundle summary -o json +{ + "id": "main.myschema.myservice", + "url": "[DATABRICKS_URL]/explore/data/model-provider-services/main/myschema/myservice?w=[NUMID]" +} + +=== Verify deployment +>>> [CLI] ai-gateway get-model-provider-service model-provider-services/main.myschema.myservice +{ + "name": "model-provider-services/main.myschema.myservice", + "comment": "COMMENT1" +} + +=== Update comment (should update in place, not recreate) +>>> update_file.py databricks.yml COMMENT1 COMMENT2 + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Updated model_provider_services.mps1 +Files: 1 uploaded, 0 deleted +Resources: 0 created, 1 changed, 0 deleted, 0 unchanged + +>>> print_requests.py //unity +{ + "method": "PATCH", + "path": "/api/2.1/unity-catalog/model-provider-services/main.myschema.myservice", + "q": { + "update_mask": "*" + }, + "body": { + "comment": "COMMENT2", + "config": { + "custom": { + "direct": { + "api_key": { + "plaintext": "dummy-api-key" + }, + "base_url": "https://api.example.com/v1" + } + }, + "provider_type": "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", + "targets": [ + { + "model": "gpt-4o", + "native_api_types": [ + "openai/v1/chat/completions" + ] + } + ] + } + } +} + +>>> [CLI] ai-gateway get-model-provider-service model-provider-services/main.myschema.myservice +"COMMENT2" + +=== Change an immutable field (should plan a recreate) +>>> update_file.py databricks.yml EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC + +>>> [CLI] bundle plan +recreate model_provider_services.mps1 + +Plan: 1 to add, 0 to change, 1 to delete, 0 unchanged + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete resources.model_provider_services.mps1 + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default + +Destroy: 1 deleted + +>>> print_requests.py //unity +{ + "method": "DELETE", + "path": "/api/2.1/unity-catalog/model-provider-services/main.myschema.myservice" +} diff --git a/acceptance/bundle/resources/model_provider_services/basic/script b/acceptance/bundle/resources/model_provider_services/basic/script new file mode 100644 index 00000000000..f12527b141d --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/basic/script @@ -0,0 +1,29 @@ +title "Initial summary before deploy" +trace $CLI bundle summary -o json | jq .resources.model_provider_services.mps1 + +title "Verify it does not exist yet" +trace musterr $CLI ai-gateway get-model-provider-service model-provider-services/main.myschema.myservice + +trace $CLI bundle deploy +trace print_requests.py //unity + +title "Summary should show the id and the Catalog Explorer url" +trace $CLI bundle summary -o json | jq ".resources.model_provider_services.mps1 | {id, url}" + +title "Verify deployment" +trace $CLI ai-gateway get-model-provider-service model-provider-services/main.myschema.myservice | jq '{name, comment}' + +title "Update comment (should update in place, not recreate)" +trace update_file.py databricks.yml COMMENT1 COMMENT2 +trace $CLI bundle deploy +trace print_requests.py //unity +trace $CLI ai-gateway get-model-provider-service model-provider-services/main.myschema.myservice | jq .comment + +title "Change an immutable field (should plan a recreate)" +trace update_file.py databricks.yml EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC +trace $CLI bundle plan + +trace $CLI bundle destroy --auto-approve +trace print_requests.py //unity + +rm -f out.requests.txt diff --git a/acceptance/bundle/resources/model_provider_services/basic/test.toml b/acceptance/bundle/resources/model_provider_services/basic/test.toml new file mode 100644 index 00000000000..116f2013783 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/basic/test.toml @@ -0,0 +1,7 @@ +# Local only: this test inspects the recorded request stream (print_requests), +# which has no equivalent against a real workspace. +Cloud = false + +Ignore = [ + ".databricks", +] diff --git a/acceptance/bundle/resources/model_provider_services/lifecycle/databricks.yml.tmpl b/acceptance/bundle/resources/model_provider_services/lifecycle/databricks.yml.tmpl new file mode 100644 index 00000000000..c7e4a7552c7 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/lifecycle/databricks.yml.tmpl @@ -0,0 +1,19 @@ +bundle: + name: deploy-model-provider-service-test-$UNIQUE_NAME + +resources: + model_provider_services: + mps: + parent: schemas/main.default + model_provider_service_id: test_mps_$UNIQUE_NAME + comment: "Routes to a custom OpenAI-compatible provider" + config: + provider_type: EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM + targets: + - model: gpt-4o + native_api_types: ["openai/v1/chat/completions"] + custom: + direct: + base_url: https://api.example.com/v1 + api_key: + plaintext: dummy-api-key diff --git a/acceptance/bundle/resources/model_provider_services/lifecycle/out.test.toml b/acceptance/bundle/resources/model_provider_services/lifecycle/out.test.toml new file mode 100644 index 00000000000..ae5c7bd798f --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/lifecycle/out.test.toml @@ -0,0 +1,3 @@ +Cloud = true +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.DMS = ["", "true"] diff --git a/acceptance/bundle/resources/model_provider_services/lifecycle/output.txt b/acceptance/bundle/resources/model_provider_services/lifecycle/output.txt new file mode 100644 index 00000000000..7ed3ee78137 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/lifecycle/output.txt @@ -0,0 +1,23 @@ + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/deploy-model-provider-service-test-[UNIQUE_NAME]/default/files... +Created model_provider_services.mps +Files: 4 uploaded, 0 deleted +Resources: 1 created, 0 changed, 0 deleted, 0 unchanged + +>>> [CLI] ai-gateway get-model-provider-service model-provider-services/main.default.test_mps_[UNIQUE_NAME] +{ + "name": "model-provider-services/main.default.test_mps_[UNIQUE_NAME]", + "comment": "Routes to a custom OpenAI-compatible provider" +} + +>>> [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete resources.model_provider_services.mps + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/deploy-model-provider-service-test-[UNIQUE_NAME]/default + +Destroy: 1 deleted diff --git a/acceptance/bundle/resources/model_provider_services/lifecycle/script b/acceptance/bundle/resources/model_provider_services/lifecycle/script new file mode 100644 index 00000000000..634183f4759 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/lifecycle/script @@ -0,0 +1,17 @@ +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve +} +trap cleanup EXIT + +trace $CLI bundle deploy + +MPS_ID=$($CLI bundle summary --output json | jq -r '.resources.model_provider_services.mps.id') + +trace $CLI ai-gateway get-model-provider-service "model-provider-services/$MPS_ID" | jq '{name, comment}' + +# Verify there is no drift right after deploy. Text plan (not -o json) keeps the +# output identical between the mock and a real workspace: the server populates +# output-only fields but the resource action stays "unchanged". +trace $CLI bundle plan diff --git a/acceptance/bundle/resources/model_provider_services/lifecycle/test.toml b/acceptance/bundle/resources/model_provider_services/lifecycle/test.toml new file mode 100644 index 00000000000..68085ea7337 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/lifecycle/test.toml @@ -0,0 +1,9 @@ +# Model provider service lifecycle also runs against a real workspace. It uses a +# custom OpenAI-compatible provider with placeholder base_url/api_key, the +# smallest config the backend accepts (it cannot be created with only a comment). +RecordRequests = false + +Ignore = [ + "databricks.yml", + ".databricks", +] diff --git a/acceptance/bundle/resources/model_provider_services/remote-delete/databricks.yml b/acceptance/bundle/resources/model_provider_services/remote-delete/databricks.yml new file mode 100644 index 00000000000..aeac81263d9 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/remote-delete/databricks.yml @@ -0,0 +1,20 @@ +bundle: + name: test-bundle + +resources: + model_provider_services: + mps1: + parent: schemas/main.myschema + model_provider_service_id: myservice + comment: COMMENT1 + config: + provider_type: EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM + targets: + - model: gpt-4o + native_api_types: + - openai/v1/chat/completions + custom: + direct: + base_url: https://api.example.com/v1 + api_key: + plaintext: dummy-api-key diff --git a/acceptance/bundle/resources/model_provider_services/remote-delete/out.test.toml b/acceptance/bundle/resources/model_provider_services/remote-delete/out.test.toml new file mode 100644 index 00000000000..59b56a2037c --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/remote-delete/out.test.toml @@ -0,0 +1,3 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.DMS = ["", "true"] diff --git a/acceptance/bundle/resources/model_provider_services/remote-delete/output.txt b/acceptance/bundle/resources/model_provider_services/remote-delete/output.txt new file mode 100644 index 00000000000..3b2a33e3c02 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/remote-delete/output.txt @@ -0,0 +1,20 @@ + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Created model_provider_services.mps1 +Files: 3 uploaded, 0 deleted +Resources: 1 created, 0 changed, 0 deleted, 0 unchanged + +=== Delete the resource out of band +>>> [CLI] ai-gateway delete-model-provider-service model-provider-services/main.myschema.myservice + +=== Plan should detect the resource is gone and re-create it +>>> [CLI] bundle plan +create model_provider_services.mps1 + +Plan: 1 to add, 0 to change, 0 to delete, 0 unchanged + +>>> [CLI] bundle destroy --auto-approve +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default + +Destroy: 0 deleted diff --git a/acceptance/bundle/resources/model_provider_services/remote-delete/script b/acceptance/bundle/resources/model_provider_services/remote-delete/script new file mode 100644 index 00000000000..e7377733cc6 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/remote-delete/script @@ -0,0 +1,9 @@ +trace $CLI bundle deploy + +title "Delete the resource out of band" +trace $CLI ai-gateway delete-model-provider-service model-provider-services/main.myschema.myservice + +title "Plan should detect the resource is gone and re-create it" +trace $CLI bundle plan + +trace $CLI bundle destroy --auto-approve diff --git a/acceptance/bundle/resources/model_provider_services/remote-delete/test.toml b/acceptance/bundle/resources/model_provider_services/remote-delete/test.toml new file mode 100644 index 00000000000..074377ab341 --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/remote-delete/test.toml @@ -0,0 +1,9 @@ +# Local only: simulates an out-of-band delete with a fixed resource name and no +# cloud-valid config, so it can't run against a real workspace. +Cloud = false + +RecordRequests = false + +Ignore = [ + ".databricks", +] diff --git a/acceptance/bundle/resources/model_provider_services/test.toml b/acceptance/bundle/resources/model_provider_services/test.toml new file mode 100644 index 00000000000..90109467fba --- /dev/null +++ b/acceptance/bundle/resources/model_provider_services/test.toml @@ -0,0 +1,7 @@ +# AI Gateway securables are only deployable via the direct deployment engine +# (there is no Terraform provider path for them in bundles). +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] + +# Lifecycle tests also run against a real workspace. Tests that inspect the +# recorded request stream (e.g. basic) can't run on cloud and override this. +Cloud = true diff --git a/acceptance/experimental/open/output.txt b/acceptance/experimental/open/output.txt index 436050964c6..f9a43b801be 100644 --- a/acceptance/experimental/open/output.txt +++ b/acceptance/experimental/open/output.txt @@ -9,7 +9,7 @@ === unknown resource type >>> [CLI] experimental open --url unknown 123 -Error: unknown resource type "unknown", must be one of: alerts, apps, catalogs, cluster_policies, clusters, dashboards, database_catalogs, database_instances, experiments, genie_spaces, instance_pools, jobs, mcp_services, model_services, model_serving_endpoints, models, notebooks, pipelines, postgres_catalogs, postgres_synced_tables, quality_monitors, queries, registered_models, schemas, secrets, synced_database_tables, vector_search_endpoints, vector_search_indexes, volumes, warehouses +Error: unknown resource type "unknown", must be one of: alerts, apps, catalogs, cluster_policies, clusters, dashboards, database_catalogs, database_instances, experiments, genie_spaces, instance_pools, jobs, mcp_services, model_provider_services, model_services, model_serving_endpoints, models, notebooks, pipelines, postgres_catalogs, postgres_synced_tables, quality_monitors, queries, registered_models, schemas, secrets, synced_database_tables, vector_search_endpoints, vector_search_indexes, volumes, warehouses === test auto-completion handler >>> [CLI] __complete experimental open , @@ -26,6 +26,7 @@ genie_spaces instance_pools jobs mcp_services +model_provider_services model_services model_serving_endpoints models diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index f54d1b5a624..c71bb2c5834 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -27,6 +27,7 @@ var unsupportedResources = []string{ "registered_models", "model_services", "mcp_services", + "model_provider_services", "database_catalogs", "synced_database_tables", "postgres_branches", diff --git a/bundle/config/mutator/resourcemutator/apply_target_mode_test.go b/bundle/config/mutator/resourcemutator/apply_target_mode_test.go index a0d3132af2b..27d2837ffb1 100644 --- a/bundle/config/mutator/resourcemutator/apply_target_mode_test.go +++ b/bundle/config/mutator/resourcemutator/apply_target_mode_test.go @@ -114,6 +114,9 @@ func mockBundle(mode config.Mode) *bundle.Bundle { McpServices: map[string]*resources.McpService{ "mcpservice1": {McpServiceConfig: resources.McpServiceConfig{Parent: "schemas/main.default", McpServiceId: "mcpservice1"}}, }, + ModelProviderServices: map[string]*resources.ModelProviderService{ + "modelproviderservice1": {ModelProviderServiceConfig: resources.ModelProviderServiceConfig{Parent: "schemas/main.default", ModelProviderServiceId: "modelproviderservice1"}}, + }, RegisteredModels: map[string]*resources.RegisteredModel{ "registeredmodel1": {CreateRegisteredModelRequest: catalog.CreateRegisteredModelRequest{Name: "registeredmodel1"}}, }, diff --git a/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go b/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go index 5d95b8acc2f..488e215a2e2 100644 --- a/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go +++ b/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go @@ -101,11 +101,11 @@ func splitUCName(name string, n int) ([]string, bool) { return parts, true } -// resolveParent rewrites a `schemas/{catalog}.{schema}` parent reference so that +// resolveAiGatewayParent rewrites a `schemas/{catalog}.{schema}` parent reference so that // a catalog or schema defined in the same bundle becomes an explicit deploy-time // dependency. AI Gateway securables address their parent schema with this // compound field rather than separate catalog/schema fields. -func resolveParent(b *bundle.Bundle, parent string) string { +func resolveAiGatewayParent(b *bundle.Bundle, parent string) string { rest, ok := strings.CutPrefix(parent, "schemas/") if !ok { return parent @@ -215,13 +215,19 @@ func (m *captureUCDependencies) Apply(ctx context.Context, b *bundle.Bundle) dia if dynvar.ContainsVariableReference(ms.Parent) { continue } - ms.Parent = resolveParent(b, ms.Parent) + ms.Parent = resolveAiGatewayParent(b, ms.Parent) } for _, ms := range b.Config.Resources.McpServices { if ms == nil { continue } - ms.Parent = resolveParent(b, ms.Parent) + ms.Parent = resolveAiGatewayParent(b, ms.Parent) + } + for _, mps := range b.Config.Resources.ModelProviderServices { + if mps == nil { + continue + } + mps.Parent = resolveAiGatewayParent(b, mps.Parent) } // Schemas are resolved last because the schema catalog resolution modifies diff --git a/bundle/config/mutator/resourcemutator/capture_uc_dependencies_test.go b/bundle/config/mutator/resourcemutator/capture_uc_dependencies_test.go index d8c39e3276b..e5281a42a77 100644 --- a/bundle/config/mutator/resourcemutator/capture_uc_dependencies_test.go +++ b/bundle/config/mutator/resourcemutator/capture_uc_dependencies_test.go @@ -147,6 +147,11 @@ func TestCaptureUCDependencies(t *testing.T) { Parent: "schemas/mycatalog.myschema", McpServiceId: "mymcp", }}, }, + ModelProviderServices: map[string]*resources.ModelProviderService{ + "my_mps": {ModelProviderServiceConfig: resources.ModelProviderServiceConfig{ + Parent: "schemas/mycatalog.myschema", ModelProviderServiceId: "mymps", + }}, + }, }, }, } @@ -188,6 +193,9 @@ func TestCaptureUCDependencies(t *testing.T) { // MCP service (same compound parent field). assert.Equal(t, "schemas/"+catalogRef+"."+schemaRef, b.Config.Resources.McpServices["my_mcp_service"].Parent) + + // Model provider service (same compound parent field). + assert.Equal(t, "schemas/"+catalogRef+"."+schemaRef, b.Config.Resources.ModelProviderServices["my_mps"].Parent) } // Pipeline schema and target are mutually exclusive; only the populated field diff --git a/bundle/config/mutator/resourcemutator/run_as_test.go b/bundle/config/mutator/resourcemutator/run_as_test.go index d32f8538060..e5cbbd76878 100644 --- a/bundle/config/mutator/resourcemutator/run_as_test.go +++ b/bundle/config/mutator/resourcemutator/run_as_test.go @@ -49,6 +49,7 @@ func allResourceTypes(t *testing.T) []string { "job_runs", "jobs", "mcp_services", + "model_provider_services", "model_services", "model_serving_endpoints", "models", @@ -191,6 +192,7 @@ var allowList = []string{ "models", "model_services", "mcp_services", + "model_provider_services", "postgres_branches", "postgres_catalogs", "postgres_databases", diff --git a/bundle/config/resources.go b/bundle/config/resources.go index db61c4bdbd1..f13a44617c4 100644 --- a/bundle/config/resources.go +++ b/bundle/config/resources.go @@ -20,6 +20,7 @@ type Resources struct { ModelServingEndpoints map[string]*resources.ModelServingEndpoint `json:"model_serving_endpoints,omitempty"` ModelServices map[string]*resources.ModelService `json:"model_services,omitempty"` McpServices map[string]*resources.McpService `json:"mcp_services,omitempty"` + ModelProviderServices map[string]*resources.ModelProviderService `json:"model_provider_services,omitempty"` RegisteredModels map[string]*resources.RegisteredModel `json:"registered_models,omitempty"` QualityMonitors map[string]*resources.QualityMonitor `json:"quality_monitors,omitempty"` Catalogs map[string]*resources.Catalog `json:"catalogs,omitempty"` @@ -115,6 +116,7 @@ func (r *Resources) AllResources() []ResourceGroup { collectResourceMap(descriptions["model_serving_endpoints"], r.ModelServingEndpoints), collectResourceMap(descriptions["model_services"], r.ModelServices), collectResourceMap(descriptions["mcp_services"], r.McpServices), + collectResourceMap(descriptions["model_provider_services"], r.ModelProviderServices), collectResourceMap(descriptions["registered_models"], r.RegisteredModels), collectResourceMap(descriptions["quality_monitors"], r.QualityMonitors), collectResourceMap(descriptions["catalogs"], r.Catalogs), @@ -189,6 +191,7 @@ func SupportedResources() map[string]resources.ResourceDescription { "model_serving_endpoints": (&resources.ModelServingEndpoint{}).ResourceDescription(), "model_services": (&resources.ModelService{}).ResourceDescription(), "mcp_services": (&resources.McpService{}).ResourceDescription(), + "model_provider_services": (&resources.ModelProviderService{}).ResourceDescription(), "registered_models": (&resources.RegisteredModel{}).ResourceDescription(), "quality_monitors": (&resources.QualityMonitor{}).ResourceDescription(), "catalogs": (&resources.Catalog{}).ResourceDescription(), diff --git a/bundle/config/resources/model_provider_service.go b/bundle/config/resources/model_provider_service.go new file mode 100644 index 00000000000..3339f2ae9a8 --- /dev/null +++ b/bundle/config/resources/model_provider_service.go @@ -0,0 +1,104 @@ +package resources + +import ( + "context" + "net/url" + + "github.com/databricks/cli/libs/log" + "github.com/databricks/cli/libs/workspaceurls" + "github.com/databricks/databricks-sdk-go" + "github.com/databricks/databricks-sdk-go/apierr" + "github.com/databricks/databricks-sdk-go/marshal" + "github.com/databricks/databricks-sdk-go/service/catalog" +) + +// ModelProviderServiceConfig is the bundle-authored state for an AI Gateway +// model provider service. +// +// It mirrors ModelServiceConfig: the SDK models the create inputs `parent` and +// `model_provider_service_id` as URL parameters (`json:"-"`) outside the +// ModelProviderService body and derives the resource `name` +// (`model-provider-services/{catalog}.{schema}.{model_provider_service}`) +// server-side, so we expose a flat struct with the immutable identity plus the +// mutable body. Owner is not exposed yet (the API returns effective_owner on +// read, not owner). +// +// Note: config.provider_type is immutable after create (the API rejects +// changing it via Update); it is classified recreate_on_changes in +// resources.yml. +type ModelProviderServiceConfig struct { + // Parent schema, format `schemas/{catalog}.{schema}`. Immutable: the server + // derives `name` from parent + model_provider_service_id, so changing it + // recreates the resource. + Parent string `json:"parent"` + // Leaf id of the model provider service, e.g. "openai_prod". Immutable. + ModelProviderServiceId string `json:"model_provider_service_id"` + // User-provided description. + Comment string `json:"comment,omitempty"` + // Behavioral configuration: provider connection, model catalog, passthrough + // policy. config.provider_type is required at create and immutable after. + Config *catalog.ModelProviderServiceConfig `json:"config,omitempty"` + + ForceSendFields []string `json:"-" url:"-"` +} + +func (c *ModelProviderServiceConfig) UnmarshalJSON(b []byte) error { + return marshal.Unmarshal(b, c) +} + +func (c ModelProviderServiceConfig) MarshalJSON() ([]byte, error) { + return marshal.Marshal(c) +} + +type ModelProviderService struct { + BaseResource + ModelProviderServiceConfig +} + +// UnmarshalJSON / MarshalJSON are defined on the wrapper so it does not inherit +// ModelProviderServiceConfig's promoted marshaler, which would silently drop the +// BaseResource fields (id, url, lifecycle, modified_status). +func (m *ModelProviderService) UnmarshalJSON(b []byte) error { + return marshal.Unmarshal(b, m) +} + +func (m ModelProviderService) MarshalJSON() ([]byte, error) { + return marshal.Marshal(m) +} + +func (m *ModelProviderService) Exists(ctx context.Context, w *databricks.WorkspaceClient, id string) (bool, error) { + // The engine tracks the id as the bare + // {catalog}.{schema}.{model_provider_service}; the API addresses the + // resource by its full name. + _, err := w.AiGateway.GetModelProviderService(ctx, catalog.GetModelProviderServiceRequest{Name: "model-provider-services/" + id}) + if err != nil { + log.Debugf(ctx, "model provider service %s does not exist", id) + if apierr.IsMissing(err) { + return false, nil + } + return false, err + } + return true, nil +} + +func (*ModelProviderService) ResourceDescription() ResourceDescription { + return ResourceDescription{ + SingularName: "model_provider_service", + PluralName: "model_provider_services", + SingularTitle: "Model provider service", + PluralTitle: "Model provider services", + } +} + +func (m *ModelProviderService) InitializeURL(baseURL url.URL) { + if m.ID == "" { + return + } + // The id is the bare {catalog}.{schema}.{model_provider_service}; ResourceURL + // splits it into the Catalog Explorer path explore/data/model-provider-services/... + m.URL = workspaceurls.ResourceURL(baseURL, "model_provider_services", m.ID) +} + +func (m *ModelProviderService) GetName() string { + return m.ModelProviderServiceId +} diff --git a/bundle/config/resources_test.go b/bundle/config/resources_test.go index fb89d49c3c5..c666c8964c0 100644 --- a/bundle/config/resources_test.go +++ b/bundle/config/resources_test.go @@ -253,6 +253,11 @@ func TestResourcesBindSupport(t *testing.T) { McpServiceConfig: resources.McpServiceConfig{}, }, }, + ModelProviderServices: map[string]*resources.ModelProviderService{ + "my_model_provider_service": { + ModelProviderServiceConfig: resources.ModelProviderServiceConfig{}, + }, + }, SecretScopes: map[string]*resources.SecretScope{ "my_secret_scope": { Name: "0", @@ -402,6 +407,7 @@ func TestResourcesBindSupport(t *testing.T) { m.GetMockServingEndpointsAPI().EXPECT().Get(mock.Anything, mock.Anything).Return(nil, nil) m.GetMockAiGatewayAPI().EXPECT().GetModelService(mock.Anything, mock.Anything).Return(nil, nil) m.GetMockAiGatewayAPI().EXPECT().GetMcpService(mock.Anything, mock.Anything).Return(nil, nil) + m.GetMockAiGatewayAPI().EXPECT().GetModelProviderService(mock.Anything, mock.Anything).Return(nil, nil) m.GetMockSecretsAPI().EXPECT().ListScopesAll(mock.Anything).Return([]workspace.SecretScope{ {Name: "0"}, }, nil) diff --git a/bundle/deploy/terraform/lifecycle_test.go b/bundle/deploy/terraform/lifecycle_test.go index dc592750113..508d9610f87 100644 --- a/bundle/deploy/terraform/lifecycle_test.go +++ b/bundle/deploy/terraform/lifecycle_test.go @@ -28,6 +28,7 @@ func TestConvertLifecycleForAllResources(t *testing.T) { // AI Gateway model service is deployed through the direct engine only. "model_services", "mcp_services", + "model_provider_services", "secrets", "vector_search_endpoints", "vector_search_indexes", diff --git a/bundle/direct/dresources/all.go b/bundle/direct/dresources/all.go index 615a704c6ca..942f7f1f648 100644 --- a/bundle/direct/dresources/all.go +++ b/bundle/direct/dresources/all.go @@ -38,6 +38,7 @@ var SupportedResources = map[string]any{ "model_serving_endpoints": (*ResourceModelServingEndpoint)(nil), "model_services": (*ResourceModelService)(nil), "mcp_services": (*ResourceMcpService)(nil), + "model_provider_services": (*ResourceModelProviderService)(nil), "quality_monitors": (*ResourceQualityMonitor)(nil), "vector_search_endpoints": (*ResourceVectorSearchEndpoint)(nil), "vector_search_indexes": (*ResourceVectorSearchIndex)(nil), diff --git a/bundle/direct/dresources/all_test.go b/bundle/direct/dresources/all_test.go index 92229daad93..de607f04fa5 100644 --- a/bundle/direct/dresources/all_test.go +++ b/bundle/direct/dresources/all_test.go @@ -114,6 +114,14 @@ var testConfig map[string]any = map[string]any{ }, }, + "model_provider_services": &resources.ModelProviderService{ + ModelProviderServiceConfig: resources.ModelProviderServiceConfig{ + Parent: "schemas/main.default", + ModelProviderServiceId: "my_model_provider_service", + Comment: "Test model provider service", + }, + }, + "registered_models": &resources.RegisteredModel{ CreateRegisteredModelRequest: catalog.CreateRegisteredModelRequest{ Name: "my_registered_model", diff --git a/bundle/direct/dresources/apitypes.generated.yml b/bundle/direct/dresources/apitypes.generated.yml index 4d5aa8f4b7a..af96326fa4c 100644 --- a/bundle/direct/dresources/apitypes.generated.yml +++ b/bundle/direct/dresources/apitypes.generated.yml @@ -30,6 +30,8 @@ jobs: jobs.JobSettings mcp_services: catalog.CreateMcpServiceRequest +model_provider_services: catalog.CreateModelProviderServiceRequest + model_services: catalog.CreateModelServiceRequest model_serving_endpoints: serving.CreateServingEndpoint diff --git a/bundle/direct/dresources/apitypes.yml b/bundle/direct/dresources/apitypes.yml index a7257a525ae..ba16fd72c6a 100644 --- a/bundle/direct/dresources/apitypes.yml +++ b/bundle/direct/dresources/apitypes.yml @@ -13,6 +13,8 @@ genie_spaces: dashboards.GenieSpace mcp_services: catalog.McpService +model_provider_services: catalog.ModelProviderService + model_services: catalog.ModelService postgres_branches: postgres.BranchSpec diff --git a/bundle/direct/dresources/configs/model_provider_services.generated.yml b/bundle/direct/dresources/configs/model_provider_services.generated.yml new file mode 100644 index 00000000000..4bb080d9e8f --- /dev/null +++ b/bundle/direct/dresources/configs/model_provider_services.generated.yml @@ -0,0 +1,30 @@ +# Generated, do not edit. + +recreate_on_changes: + - field: config.provider_type + reason: spec:immutable + +ignore_remote_changes: + - field: config.amazon_bedrock.direct.aws_access_key.secret_access_key.plaintext + reason: spec:input_only + - field: config.anthropic.direct.api_key.plaintext + reason: spec:input_only + - field: config.azure_openai.direct.api_key.plaintext + reason: spec:input_only + - field: config.azure_openai.direct.entra_service_principal.client_secret.plaintext + reason: spec:input_only + - field: config.custom.direct.api_key.plaintext + reason: spec:input_only + - field: config.gemini_enterprise.direct.api_key.plaintext + reason: spec:input_only + - field: config.microsoft_foundry.direct.api_key.plaintext + reason: spec:input_only + - field: config.microsoft_foundry.direct.entra_service_principal.client_secret.plaintext + reason: spec:input_only + - field: config.openai.direct.api_key.plaintext + reason: spec:input_only + + - field: config.inference_table.is_deleted + reason: spec:output_only + - field: config.inference_table.table + reason: spec:output_only diff --git a/bundle/direct/dresources/configs/model_provider_services.yml b/bundle/direct/dresources/configs/model_provider_services.yml new file mode 100644 index 00000000000..f83495c340f --- /dev/null +++ b/bundle/direct/dresources/configs/model_provider_services.yml @@ -0,0 +1,9 @@ +provided_id_fields: + # parent + model_provider_service_id compose the server-derived resource + # name (model-provider-services/{catalog}.{schema}.{model_provider_service}), + # which is the ID the resource is fetched by. Both immutable; a local + # change recreates. DoRead reconstructs them from the returned name. + - field: parent + reason: id_field + - field: model_provider_service_id + reason: id_field diff --git a/bundle/direct/dresources/model_provider_service.go b/bundle/direct/dresources/model_provider_service.go new file mode 100644 index 00000000000..ebf02e9c3f6 --- /dev/null +++ b/bundle/direct/dresources/model_provider_service.go @@ -0,0 +1,138 @@ +package dresources + +import ( + "context" + "fmt" + "strings" + + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go" + "github.com/databricks/databricks-sdk-go/common/types/fieldmask" + "github.com/databricks/databricks-sdk-go/service/catalog" +) + +// AI Gateway model provider service. +// API: https://docs.databricks.com/api/workspace/aigateway +// Terraform: databricks_ai_gateway_model_provider_service +// +// Mirrors ResourceModelService: the remote type returned by DoRead is the same +// bundle-local resources.ModelProviderServiceConfig used for state (so +// RemapState is not needed), and DoRead reconstructs the create-time identity +// (parent + model_provider_service_id) from the server-derived resource name. +const modelProviderServiceNamePrefix = "model-provider-services/" + +type ResourceModelProviderService struct { + client *databricks.WorkspaceClient +} + +func (*ResourceModelProviderService) New(client *databricks.WorkspaceClient) *ResourceModelProviderService { + return &ResourceModelProviderService{client: client} +} + +func (*ResourceModelProviderService) PrepareState(input *resources.ModelProviderService) *resources.ModelProviderServiceConfig { + return &input.ModelProviderServiceConfig +} + +// modelProviderServiceIdentityFromName reconstructs the create-time parent and +// leaf id from the server-derived resource name +// `model-provider-services/{catalog}.{schema}.{model_provider_service}`. +func modelProviderServiceIdentityFromName(name string) (parent, modelProviderServiceId string, err error) { + rest, ok := strings.CutPrefix(name, modelProviderServiceNamePrefix) + if !ok { + return "", "", fmt.Errorf("unexpected model provider service name %q (want model-provider-services/{catalog}.{schema}.{model_provider_service})", name) + } + parts := strings.Split(rest, ".") + if len(parts) != 3 { + return "", "", fmt.Errorf("unexpected model provider service name %q (want three dot-separated components)", name) + } + return "schemas/" + parts[0] + "." + parts[1], parts[2], nil +} + +func responseToModelProviderServiceConfig(ms *catalog.ModelProviderService) (*resources.ModelProviderServiceConfig, error) { + parent, id, err := modelProviderServiceIdentityFromName(ms.Name) + if err != nil { + return nil, err + } + return &resources.ModelProviderServiceConfig{ + Parent: parent, + ModelProviderServiceId: id, + Comment: ms.Comment, + Config: ms.Config, + ForceSendFields: nil, + }, nil +} + +// modelProviderServiceBody builds the ModelProviderService write payload from the +// bundle config. Only comment and config are client-settable; every other field +// is OUTPUT_ONLY (server-derived) and sent as its zero value. +func modelProviderServiceBody(config *resources.ModelProviderServiceConfig) catalog.ModelProviderService { + return catalog.ModelProviderService{ + Comment: config.Comment, + Config: config.Config, + CreateTime: nil, + CreatedBy: "", + EffectiveOwner: "", + Etag: "", + MetastoreId: "", + Name: "", + UpdateTime: nil, + UpdatedBy: "", + ForceSendFields: nil, + } +} + +func (r *ResourceModelProviderService) DoRead(ctx context.Context, id string) (*resources.ModelProviderServiceConfig, error) { + ms, err := r.client.AiGateway.GetModelProviderService(ctx, catalog.GetModelProviderServiceRequest{Name: modelProviderServiceNamePrefix + id}) + if err != nil { + return nil, err + } + return responseToModelProviderServiceConfig(ms) +} + +func (r *ResourceModelProviderService) DoCreate(ctx context.Context, config *resources.ModelProviderServiceConfig) (string, *resources.ModelProviderServiceConfig, error) { + resp, err := r.client.AiGateway.CreateModelProviderService(ctx, catalog.CreateModelProviderServiceRequest{ + Parent: config.Parent, + ModelProviderServiceId: config.ModelProviderServiceId, + ModelProviderService: modelProviderServiceBody(config), + }) + if err != nil { + return "", nil, err + } + state, err := responseToModelProviderServiceConfig(resp) + if err != nil { + return "", nil, err + } + return strings.TrimPrefix(resp.Name, modelProviderServiceNamePrefix), state, nil +} + +// DoUpdate sends update_mask "*" on every update. name, parent and +// model_provider_service_id are immutable (provided_id_fields), and +// config.provider_type is immutable (recreate_on_changes), so the wildcard +// replaces every client-settable field (comment + a full config replace), +// matching the mask the Terraform provider generates. +// +// Etag is intentionally left empty here and in DoDelete: an empty etag means no +// If-Match precondition (last-write-wins), matching the Terraform provider, +// which also does not send etag. We deliberately do not do optimistic +// concurrency on these resources. +func (r *ResourceModelProviderService) DoUpdate(ctx context.Context, id string, config *resources.ModelProviderServiceConfig, _ *PlanEntry) (*resources.ModelProviderServiceConfig, error) { + resp, err := r.client.AiGateway.UpdateModelProviderService(ctx, catalog.UpdateModelProviderServiceRequest{ + Etag: "", + Name: modelProviderServiceNamePrefix + id, + ModelProviderService: modelProviderServiceBody(config), + UpdateMask: fieldmask.FieldMask{Paths: []string{"*"}}, + ForceSendFields: nil, + }) + if err != nil { + return nil, err + } + return responseToModelProviderServiceConfig(resp) +} + +func (r *ResourceModelProviderService) DoDelete(ctx context.Context, id string, _ *resources.ModelProviderServiceConfig) error { + return r.client.AiGateway.DeleteModelProviderService(ctx, catalog.DeleteModelProviderServiceRequest{ + Etag: "", + Name: modelProviderServiceNamePrefix + id, + ForceSendFields: nil, + }) +} diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index 80fb71509fa..07cbbe6467e 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -1213,6 +1213,25 @@ resources: "parent": "description": |- PLACEHOLDER + "model_provider_services": + "description": |- + PLACEHOLDER + "$fields": + "comment": + "description": |- + PLACEHOLDER + "config": + "description": |- + PLACEHOLDER + "lifecycle": + "description": |- + PLACEHOLDER + "model_provider_service_id": + "description": |- + PLACEHOLDER + "parent": + "description": |- + PLACEHOLDER "model_services": "description": |- PLACEHOLDER diff --git a/bundle/internal/validation/generated/enum_fields.go b/bundle/internal/validation/generated/enum_fields.go index 5b1d69131f2..464afe2f97d 100644 --- a/bundle/internal/validation/generated/enum_fields.go +++ b/bundle/internal/validation/generated/enum_fields.go @@ -161,6 +161,10 @@ var EnumFields = map[string][]string{ "resources.mcp_services.*.config.rate_limits[*].key": {"RATE_LIMIT_KEY_SERVICE", "RATE_LIMIT_KEY_SERVICE_PRINCIPAL", "RATE_LIMIT_KEY_USER", "RATE_LIMIT_KEY_USER_DEFAULT", "RATE_LIMIT_KEY_USER_GROUP"}, "resources.mcp_services.*.config.rate_limits[*].renewal_period": {"RATE_LIMIT_RENEWAL_PERIOD_HOUR", "RATE_LIMIT_RENEWAL_PERIOD_MINUTE"}, + "resources.model_provider_services.*.config.provider_type": {"EXTERNAL_MODEL_PROVIDER_TYPE_AMAZON_BEDROCK", "EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC", "EXTERNAL_MODEL_PROVIDER_TYPE_AZURE_OPENAI", "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", "EXTERNAL_MODEL_PROVIDER_TYPE_GEMINI_ENTERPRISE", "EXTERNAL_MODEL_PROVIDER_TYPE_MICROSOFT_FOUNDRY", "EXTERNAL_MODEL_PROVIDER_TYPE_OPENAI"}, + "resources.model_provider_services.*.config.rate_limits[*].key": {"RATE_LIMIT_KEY_SERVICE", "RATE_LIMIT_KEY_SERVICE_PRINCIPAL", "RATE_LIMIT_KEY_USER", "RATE_LIMIT_KEY_USER_DEFAULT", "RATE_LIMIT_KEY_USER_GROUP"}, + "resources.model_provider_services.*.config.rate_limits[*].renewal_period": {"RATE_LIMIT_RENEWAL_PERIOD_HOUR", "RATE_LIMIT_RENEWAL_PERIOD_MINUTE"}, + "resources.model_services.*.config.rate_limits[*].key": {"RATE_LIMIT_KEY_SERVICE", "RATE_LIMIT_KEY_SERVICE_PRINCIPAL", "RATE_LIMIT_KEY_USER", "RATE_LIMIT_KEY_USER_DEFAULT", "RATE_LIMIT_KEY_USER_GROUP"}, "resources.model_services.*.config.rate_limits[*].renewal_period": {"RATE_LIMIT_RENEWAL_PERIOD_HOUR", "RATE_LIMIT_RENEWAL_PERIOD_MINUTE"}, "resources.model_services.*.config.routing.destinations[*].destination_type": {"DESTINATION_TYPE_EXTERNAL_FOUNDATION_MODEL", "DESTINATION_TYPE_PAY_PER_TOKEN_FOUNDATION_MODEL", "DESTINATION_TYPE_PROVISIONED_THROUGHPUT_FOUNDATION_MODEL"}, diff --git a/bundle/internal/validation/generated/required_fields.go b/bundle/internal/validation/generated/required_fields.go index a0ce0e286d3..5617f9f5ffa 100644 --- a/bundle/internal/validation/generated/required_fields.go +++ b/bundle/internal/validation/generated/required_fields.go @@ -207,6 +207,14 @@ var RequiredFields = map[string][]string{ "resources.mcp_services.*.config.rate_limits[*]": {"key", "renewal_period"}, "resources.mcp_services.*.config.source_connection": {"name"}, + "resources.model_provider_services.*": {"parent", "model_provider_service_id"}, + "resources.model_provider_services.*.config.amazon_bedrock.direct.service_credential": {"name"}, + "resources.model_provider_services.*.config.azure_openai.direct.service_credential": {"name"}, + "resources.model_provider_services.*.config.inference_table": {"parent"}, + "resources.model_provider_services.*.config.microsoft_foundry.direct.service_credential": {"name"}, + "resources.model_provider_services.*.config.rate_limits[*]": {"key", "renewal_period"}, + "resources.model_provider_services.*.config.targets[*]": {"model"}, + "resources.model_services.*": {"parent", "model_service_id"}, "resources.model_services.*.config.inference_table": {"parent"}, "resources.model_services.*.config.rate_limits[*]": {"key", "renewal_period"}, diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index 195b2972b16..fc398289193 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -1778,6 +1778,39 @@ } ] }, + "resources.ModelProviderService": { + "oneOf": [ + { + "type": "object", + "properties": { + "comment": { + "$ref": "#/$defs/string" + }, + "config": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfig" + }, + "lifecycle": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" + }, + "model_provider_service_id": { + "$ref": "#/$defs/string" + }, + "parent": { + "$ref": "#/$defs/string" + } + }, + "additionalProperties": false, + "required": [ + "parent", + "model_provider_service_id" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.ModelService": { "oneOf": [ { @@ -3995,6 +4028,9 @@ "mcp_services": { "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.McpService" }, + "model_provider_services": { + "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.ModelProviderService" + }, "model_services": { "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.ModelService" }, @@ -5492,6 +5528,485 @@ } ] }, + "catalog.ModelProviderServiceConfig": { + "oneOf": [ + { + "type": "object", + "description": "Behavioral configuration for a ModelProviderService: provider authentication\nand provider-specific fields, the catalog of models this provider\nservice can route to, and the passthrough policy that governs how request\nheaders, query parameters, and unmanaged subpaths cross the trust boundary\nto the upstream provider.", + "properties": { + "allow_all_targets": { + "description": "When true, accepts any model exposed by the upstream provider; `targets`\nis not required and does not restrict routability. When false, only\nmodels listed in `targets` are routable. Defaults to false.", + "$ref": "#/$defs/bool", + "x-databricks-launch-stage": "GA" + }, + "amazon_bedrock": { + "description": "Amazon Bedrock provider configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAmazonBedrockProviderConfig", + "x-databricks-launch-stage": "GA" + }, + "anthropic": { + "description": "Anthropic provider configuration. Exactly one of `direct` or `relayed` must\nbe set on Create; the two are mutually exclusive.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAnthropicProviderConfig", + "x-databricks-launch-stage": "GA" + }, + "azure_openai": { + "description": "Azure OpenAI provider configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAzureOpenAiProviderConfig", + "x-databricks-launch-stage": "GA" + }, + "custom": { + "description": "Custom OpenAI-compatible provider configuration with bearer-token\nauthentication.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigCustomProviderConfig", + "x-databricks-launch-stage": "GA" + }, + "forward_headers": { + "description": "Whether to forward incoming HTTP headers to the upstream provider. Defaults\nto false and is configured for the entire provider service, not per request.\nUpstream authentication is configured separately in the provider-specific\nconfiguration.", + "$ref": "#/$defs/bool", + "x-databricks-launch-stage": "GA" + }, + "forward_query_parameters": { + "description": "Whether to forward incoming query parameters to the upstream provider.\nDefaults to false and is configured for the entire provider service, not\nper request.", + "$ref": "#/$defs/bool", + "x-databricks-launch-stage": "GA" + }, + "forward_unmanaged_paths": { + "description": "Whether to proxy paths that AI Gateway does not recognize as configured\nprovider-native API types. Defaults to false. When true, these paths are\nforwarded unchanged to the upstream provider. When false, only\nrecognized API paths are served. Enabling this broadens the upstream API\nsurface exposed through the provider service.", + "$ref": "#/$defs/bool", + "x-databricks-launch-stage": "GA" + }, + "gemini_enterprise": { + "description": "Gemini Enterprise provider configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigGeminiEnterpriseProviderConfig", + "x-databricks-launch-stage": "GA" + }, + "inference_table": { + "description": "Payload logging configuration for requests sent directly to this provider\nservice. Requests routed through a model service are captured by that model\nservice's inference table instead.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.InferenceTableConfig", + "x-databricks-launch-stage": "GA" + }, + "microsoft_foundry": { + "description": "Microsoft Foundry provider configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigMicrosoftFoundryProviderConfig", + "x-databricks-launch-stage": "GA" + }, + "openai": { + "description": "OpenAI provider configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigOpenAiProviderConfig", + "x-databricks-launch-stage": "GA" + }, + "provider_type": { + "description": "External model provider. Required on Create and immutable thereafter. Set\nthe matching provider-specific configuration, such as `openai`,\n`azure_openai`, or `amazon_bedrock`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigExternalModelProviderType", + "x-databricks-launch-stage": "GA" + }, + "rate_limits": { + "description": "Rate limits for requests sent directly to this provider service. Requests\nrouted through a model service use that model service's rate limits instead.", + "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/catalog.RateLimit", + "x-databricks-launch-stage": "GA" + }, + "targets": { + "description": "Models and provider-native API types exposed by this provider service. Each\nentry must include at least one `native_api_types` value. When\n`allow_all_targets` is false, at least one entry is required and model\nservice destinations can reference only listed models. When\n`allow_all_targets` is true, any upstream model is routable; entries in\nthis list provide API-type metadata without restricting other models.", + "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigModelTargetConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAmazonBedrockProviderConfig": { + "oneOf": [ + { + "type": "object", + "description": "Amazon Bedrock provider configuration.", + "properties": { + "direct": { + "description": "Amazon Bedrock region and authentication configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAmazonBedrockProviderDirectConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAmazonBedrockProviderDirectConfig": { + "oneOf": [ + { + "type": "object", + "description": "Direct form of Amazon Bedrock provider config.\n\nAuthentication is one of two mutually exclusive modes, exactly one of which\nmust be supplied on Create:\n- Access keys: set `aws_access_key`, leave `service_credential` unset.\n- Unity Catalog service credential: set `service_credential.name` to the\nresource name `credentials/{name}`, leave `aws_access_key` unset. The\ncredential value lives in Unity Catalog and is referenced by name, not\nheld on this message.\nSetting more than one mode is rejected.", + "properties": { + "aws_access_key": { + "description": "AWS access-key-pair authentication. Set `access_key_id` and\n`secret_access_key.plaintext`. Mutually exclusive with\n`service_credential`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAwsAccessKey", + "x-databricks-launch-stage": "GA" + }, + "region": { + "description": "AWS region where the Bedrock endpoint is hosted (e.g., `us-east-1`).\nRequired on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + }, + "service_credential": { + "description": "Reference to a Unity Catalog service credential authorizing Bedrock\nrequests. On Create, supply `service_credential.name` in the form\n`credentials/{name}`. Required on Create when using service-credential\nauthentication; mutually exclusive with `aws_access_key`. The credential\nis referenced by name; its value is not carried here. Only\nsupported on AWS-hosted workspaces.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigServiceCredential", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAnthropicProviderConfig": { + "oneOf": [ + { + "type": "object", + "description": "Anthropic provider configuration. Exactly one of `direct` or `relayed` must\nbe set on Create; the two are mutually exclusive.", + "properties": { + "direct": { + "description": "Direct authentication with an API key supplied in\n`direct.api_key.plaintext`. Required unless `relayed` is set.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAnthropicProviderDirectConfig", + "x-databricks-launch-stage": "GA" + }, + "relayed": { + "description": "Relayed authentication. Each inference request supplies the caller's\nOAuth token, which is forwarded to Anthropic. No Anthropic credential is\nstored. Mutually exclusive with `direct`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAnthropicProviderRelayedConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAnthropicProviderDirectConfig": { + "oneOf": [ + { + "type": "object", + "description": "Direct form of Anthropic provider config.", + "properties": { + "api_key": { + "description": "Anthropic API key. Required when creating the service. Supply the value\nin `api_key.plaintext`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAnthropicProviderRelayedConfig": { + "oneOf": [ + { + "type": "object", + "description": "Relayed Anthropic provider configuration. Each inference request supplies\nthe caller's OAuth token, which is forwarded to Anthropic. No Anthropic\ncredential is stored.", + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAwsAccessKey": { + "oneOf": [ + { + "type": "object", + "description": "AWS access-key-pair auth for Amazon Bedrock: a SigV4-signing key pair.", + "properties": { + "access_key_id": { + "description": "AWS access key ID. Required on Create when using access-key auth. Treated as\nusername-equivalent (not a secret value): round-trips on reads and is\nscrubbed from audit logs.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + }, + "secret_access_key": { + "description": "AWS secret access key paired with `access_key_id`. Required when creating\na service with access-key authentication. Supply the value in\n`secret_access_key.plaintext`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAzureOpenAiProviderConfig": { + "oneOf": [ + { + "type": "object", + "description": "Azure OpenAI provider configuration.", + "properties": { + "direct": { + "description": "Azure OpenAI endpoint and authentication configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigAzureOpenAiProviderDirectConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigAzureOpenAiProviderDirectConfig": { + "oneOf": [ + { + "type": "object", + "description": "Direct form of Azure OpenAI provider config. Exactly one of three\nmutually-exclusive auth modes must be supplied on Create:\n- API key: set `api_key`, leave `entra_service_principal` and\n`service_credential` unset.\n- Entra ID (service principal): set `entra_service_principal`, leave\n`api_key` and `service_credential` unset.\n- Unity Catalog service credential: set `service_credential.name` to the\nresource name `credentials/{name}`, leave `api_key` and\n`entra_service_principal` unset. The credential value lives in Unity\nCatalog and is referenced by name, not held on this message. Only\nsupported on Azure-hosted workspaces.\nSetting more than one mode is rejected.", + "properties": { + "api_key": { + "description": "Azure OpenAI API key. Supply the value in `api_key.plaintext`. Mutually\nexclusive with Entra ID and Unity Catalog service credential\nauthentication.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + }, + "base_url": { + "description": "Full Azure OpenAI endpoint base URL, e.g.\n`https://myresource.openai.azure.com`. Required on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + }, + "entra_service_principal": { + "description": "Entra ID service-principal authentication. Set `tenant_id`, `client_id`,\nand `client_secret.plaintext`. Mutually exclusive with `api_key` and\n`service_credential`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigEntraServicePrincipal", + "x-databricks-launch-stage": "GA" + }, + "service_credential": { + "description": "Reference to a Unity Catalog service credential authorizing Azure OpenAI\nrequests. On Create, supply `service_credential.name` in the form\n`credentials/{name}`. Required on Create when using service-credential\nauthentication; mutually exclusive with `api_key` and\n`entra_service_principal`. The credential is referenced by name; its value\nis not carried here. Only supported on Azure-hosted workspaces.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigServiceCredential", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigCustomProviderConfig": { + "oneOf": [ + { + "type": "object", + "description": "Custom OpenAI-compatible provider configuration with bearer-token\nauthentication.", + "properties": { + "direct": { + "description": "Endpoint and authentication configuration for the custom provider.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigCustomProviderDirectConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigCustomProviderDirectConfig": { + "oneOf": [ + { + "type": "object", + "description": "Direct form of a custom provider configuration. Set `api_key` to the bearer\ntoken sent in the `Authorization` header.", + "properties": { + "api_key": { + "description": "Bearer token forwarded in the `Authorization` header. Supply the value\nin `api_key.plaintext`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + }, + "base_url": { + "description": "Endpoint URL of the OpenAI-compatible service (e.g.,\n`https://api.example.com/v1`). Required on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigEntraServicePrincipal": { + "oneOf": [ + { + "type": "object", + "description": "Entra ID (Azure AD) service-principal authentication. The `tenant_id` and\n`client_id` identify the service principal, and `client_secret` authenticates\nit. AI Gateway exchanges these credentials for an Entra bearer token for\nrequests to Azure OpenAI or Microsoft Foundry.", + "properties": { + "client_id": { + "description": "Entra ID client (application) ID. Required on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + }, + "client_secret": { + "description": "Entra ID client secret. Supply the value in `client_secret.plaintext`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + }, + "tenant_id": { + "description": "Entra ID (Azure AD) tenant ID. Required on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigExternalModelProviderType": { + "oneOf": [ + { + "type": "string", + "description": "External LLM provider for an EXTERNAL_FOUNDATION_MODEL destination.", + "enum": [ + "EXTERNAL_MODEL_PROVIDER_TYPE_OPENAI", + "EXTERNAL_MODEL_PROVIDER_TYPE_AZURE_OPENAI", + "EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC", + "EXTERNAL_MODEL_PROVIDER_TYPE_AMAZON_BEDROCK", + "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", + "EXTERNAL_MODEL_PROVIDER_TYPE_MICROSOFT_FOUNDRY", + "EXTERNAL_MODEL_PROVIDER_TYPE_GEMINI_ENTERPRISE" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigGeminiEnterpriseProviderConfig": { + "oneOf": [ + { + "type": "object", + "description": "Gemini Enterprise provider configuration.", + "properties": { + "direct": { + "description": "Gemini Enterprise project, region, and authentication configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig": { + "oneOf": [ + { + "type": "object", + "description": "Direct Gemini Enterprise provider configuration. An API key is required\nwhen creating the service.", + "properties": { + "api_key": { + "description": "Google Gemini Enterprise API key. Required when creating the service.\nSupply the value in `api_key.plaintext`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + }, + "project_id": { + "description": "GCP project ID hosting the Gemini Enterprise endpoint. Required on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + }, + "region": { + "description": "GCP region of the Gemini Enterprise endpoint (e.g., `us-central1`).\nRequired on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigMicrosoftFoundryProviderConfig": { + "oneOf": [ + { + "type": "object", + "description": "Microsoft Foundry provider configuration.", + "properties": { + "direct": { + "description": "Microsoft Foundry endpoint and authentication configuration.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig": { + "oneOf": [ + { + "type": "object", + "description": "Direct form of Microsoft Foundry provider config.\n\nAuthentication is one of three mutually exclusive modes, exactly one of which\nmust be supplied on Create:\n- API key: set `api_key`, leave `entra_service_principal` and\n`service_credential` unset.\n- Entra ID (service principal): set `entra_service_principal`, leave\n`api_key` and `service_credential` unset. AI Gateway exchanges these for\nan Entra bearer token on outbound requests via the OAuth2\nclient-credentials grant.\n- Unity Catalog service credential: set `service_credential.name` to the\nresource name `credentials/{name}`, leave `api_key` and\n`entra_service_principal` unset. The credential value lives in Unity\nCatalog and is referenced by name, not held on this message. Only\nsupported on Azure-hosted workspaces.\nSetting more than one mode is rejected.", + "properties": { + "api_key": { + "description": "Microsoft Foundry API key. Supply the value in `api_key.plaintext`.\nMutually exclusive with Entra ID and Unity Catalog service credential\nauthentication.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + }, + "base_url": { + "description": "Microsoft Foundry endpoint URL. Required on Create.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + }, + "entra_service_principal": { + "description": "Entra ID service-principal authentication. Set `tenant_id`, `client_id`,\nand `client_secret.plaintext`. Mutually exclusive with `api_key` and\n`service_credential`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigEntraServicePrincipal", + "x-databricks-launch-stage": "GA" + }, + "service_credential": { + "description": "Reference to a Unity Catalog service credential authorizing Microsoft\nFoundry requests. On Create, supply `service_credential.name` in the form\n`credentials/{name}`. Required on Create when using service-credential\nauthentication; mutually exclusive with `api_key` and\n`entra_service_principal`. The credential is referenced by name; its value\nis not carried here. Only supported on Azure-hosted workspaces.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigServiceCredential", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "catalog.ModelProviderServiceConfigModelTargetConfig": { "oneOf": [ { @@ -5520,6 +6035,99 @@ } ] }, + "catalog.ModelProviderServiceConfigOpenAiProviderConfig": { + "oneOf": [ + { + "type": "object", + "description": "OpenAI provider configuration.", + "properties": { + "direct": { + "description": "OpenAI configuration with an API key supplied in the request.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigOpenAiProviderDirectConfig", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigOpenAiProviderDirectConfig": { + "oneOf": [ + { + "type": "object", + "description": "Direct (inline-credentials) form of the OpenAI provider config.", + "properties": { + "api_key": { + "description": "OpenAI API key. Required when creating the service. Supply the value in\n`api_key.plaintext`.", + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigProviderSecret", + "x-databricks-launch-stage": "GA" + }, + "base_url": { + "description": "Optional custom base URL. Defaults to `https://api.openai.com/v1`. Use for\nOpenAI-API-compatible third-party endpoints or in-network proxies.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + }, + "organization": { + "description": "Optional OpenAI organization ID. When set, the platform forwards it as\nthe `OpenAI-Organization` header.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigProviderSecret": { + "oneOf": [ + { + "type": "object", + "description": "A secret value supplied as part of an inline provider config. The caller\nsupplies the value as inline `plaintext` on writes; the platform stores it\nencrypted. The `plaintext` field is `INPUT_ONLY` and never round-trips on\nreads.", + "properties": { + "plaintext": { + "description": "Inline plaintext credential. INPUT_ONLY: the value never round-trips on\nreads. Get and List responses omit `plaintext`; the enclosing secret\nobject remains present to indicate that a secret is configured.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, + "catalog.ModelProviderServiceConfigServiceCredential": { + "oneOf": [ + { + "type": "object", + "description": "The customer-owned Unity Catalog service credential a model provider service\nuses to authenticate to its provider, referenced by name.", + "properties": { + "name": { + "description": "Resource name of the bound Unity Catalog service credential, in the form\n`credentials/{name}`. Supply this field when creating the service or\nrebinding its credential. On read, it reflects the credential's current\nname.", + "$ref": "#/$defs/string", + "x-databricks-launch-stage": "GA" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "catalog.ModelServiceConfig": { "oneOf": [ { @@ -17300,6 +17908,20 @@ } ] }, + "resources.ModelProviderService": { + "oneOf": [ + { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.ModelProviderService" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "resources.ModelService": { "oneOf": [ { @@ -17975,6 +18597,20 @@ } ] }, + "catalog.ModelProviderServiceConfigModelTargetConfig": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/github.com/databricks/databricks-sdk-go/service/catalog.ModelProviderServiceConfigModelTargetConfig" + } + }, + { + "type": "string", + "pattern": "\\$\\{(var(\\._*\\p{L}+([-_]*[\\p{L}\\p{N}]+)*(\\[[0-9]+\\])*)+)\\}" + } + ] + }, "catalog.ModelServiceConfigDestinationConfig": { "oneOf": [ { diff --git a/bundle/statemgmt/state_load_test.go b/bundle/statemgmt/state_load_test.go index 0a0dafee39f..b03ff469284 100644 --- a/bundle/statemgmt/state_load_test.go +++ b/bundle/statemgmt/state_load_test.go @@ -34,6 +34,7 @@ func TestStateToBundleEmptyLocalResources(t *testing.T) { "resources.model_serving_endpoints.test_model_serving": {ID: "1"}, "resources.model_services.test_model_service": {ID: "main.default.test_model_service"}, "resources.mcp_services.test_mcp_service": {ID: "main.default.test_mcp_service"}, + "resources.model_provider_services.test_model_provider_service": {ID: "main.default.test_model_provider_service"}, "resources.registered_models.test_registered_model": {ID: "1"}, "resources.quality_monitors.test_monitor": {ID: "1"}, "resources.catalogs.test_catalog": {ID: "1"}, @@ -171,7 +172,9 @@ func TestStateToBundleEmptyLocalResources(t *testing.T) { assert.Equal(t, resources.ModifiedStatusDeleted, config.Resources.ModelServices["test_model_service"].ModifiedStatus) assert.Equal(t, "main.default.test_mcp_service", config.Resources.McpServices["test_mcp_service"].ID) + assert.Equal(t, "main.default.test_model_provider_service", config.Resources.ModelProviderServices["test_model_provider_service"].ID) assert.Equal(t, resources.ModifiedStatusDeleted, config.Resources.McpServices["test_mcp_service"].ModifiedStatus) + assert.Equal(t, resources.ModifiedStatusDeleted, config.Resources.ModelProviderServices["test_model_provider_service"].ModifiedStatus) AssertFullResourceCoverage(t, &config) } @@ -448,6 +451,14 @@ func TestStateToBundleEmptyRemoteResources(t *testing.T) { }, }, }, + ModelProviderServices: map[string]*resources.ModelProviderService{ + "test_model_provider_service": { + ModelProviderServiceConfig: resources.ModelProviderServiceConfig{ + Parent: "schemas/main.default", + ModelProviderServiceId: "test_model_provider_service", + }, + }, + }, }, } @@ -563,7 +574,9 @@ func TestStateToBundleEmptyRemoteResources(t *testing.T) { assert.Equal(t, resources.ModifiedStatusCreated, config.Resources.ModelServices["test_model_service"].ModifiedStatus) assert.Empty(t, config.Resources.McpServices["test_mcp_service"].ID) + assert.Empty(t, config.Resources.ModelProviderServices["test_model_provider_service"].ID) assert.Equal(t, resources.ModifiedStatusCreated, config.Resources.McpServices["test_mcp_service"].ModifiedStatus) + assert.Equal(t, resources.ModifiedStatusCreated, config.Resources.ModelProviderServices["test_model_provider_service"].ModifiedStatus) AssertFullResourceCoverage(t, &config) } @@ -1019,6 +1032,20 @@ func TestStateToBundleModifiedResources(t *testing.T) { }, }, }, + ModelProviderServices: map[string]*resources.ModelProviderService{ + "test_model_provider_service": { + ModelProviderServiceConfig: resources.ModelProviderServiceConfig{ + Parent: "schemas/main.default", + ModelProviderServiceId: "test_model_provider_service", + }, + }, + "test_model_provider_service_new": { + ModelProviderServiceConfig: resources.ModelProviderServiceConfig{ + Parent: "schemas/main.default", + ModelProviderServiceId: "test_model_provider_service_new", + }, + }, + }, }, } state := ExportedResourcesMap{ @@ -1083,7 +1110,9 @@ func TestStateToBundleModifiedResources(t *testing.T) { "resources.model_services.test_model_service": {ID: "main.default.test_model_service"}, "resources.model_services.test_model_service_old": {ID: "main.default.test_model_service_old"}, "resources.mcp_services.test_mcp_service": {ID: "main.default.test_mcp_service"}, + "resources.model_provider_services.test_model_provider_service": {ID: "main.default.test_model_provider_service"}, "resources.mcp_services.test_mcp_service_old": {ID: "main.default.test_mcp_service_old"}, + "resources.model_provider_services.test_model_provider_service_old": {ID: "main.default.test_model_provider_service_old"}, "resources.secrets.test_secret": {ID: "main.default.test_secret"}, "resources.secrets.test_secret_old": {ID: "main.default.test_secret_old"}, } @@ -1303,11 +1332,17 @@ func TestStateToBundleModifiedResources(t *testing.T) { assert.Equal(t, resources.ModifiedStatusCreated, config.Resources.ModelServices["test_model_service_new"].ModifiedStatus) assert.Equal(t, "main.default.test_mcp_service", config.Resources.McpServices["test_mcp_service"].ID) + assert.Equal(t, "main.default.test_model_provider_service", config.Resources.ModelProviderServices["test_model_provider_service"].ID) assert.Empty(t, config.Resources.McpServices["test_mcp_service"].ModifiedStatus) + assert.Empty(t, config.Resources.ModelProviderServices["test_model_provider_service"].ModifiedStatus) assert.Equal(t, "main.default.test_mcp_service_old", config.Resources.McpServices["test_mcp_service_old"].ID) + assert.Equal(t, "main.default.test_model_provider_service_old", config.Resources.ModelProviderServices["test_model_provider_service_old"].ID) assert.Equal(t, resources.ModifiedStatusDeleted, config.Resources.McpServices["test_mcp_service_old"].ModifiedStatus) + assert.Equal(t, resources.ModifiedStatusDeleted, config.Resources.ModelProviderServices["test_model_provider_service_old"].ModifiedStatus) assert.Empty(t, config.Resources.McpServices["test_mcp_service_new"].ID) + assert.Empty(t, config.Resources.ModelProviderServices["test_model_provider_service_new"].ID) assert.Equal(t, resources.ModifiedStatusCreated, config.Resources.McpServices["test_mcp_service_new"].ModifiedStatus) + assert.Equal(t, resources.ModifiedStatusCreated, config.Resources.ModelProviderServices["test_model_provider_service_new"].ModifiedStatus) assert.Equal(t, "main.default.test_secret", config.Resources.Secrets["test_secret"].ID) assert.Empty(t, config.Resources.Secrets["test_secret"].ModifiedStatus) diff --git a/cmd/experimental/workspace_open_test.go b/cmd/experimental/workspace_open_test.go index 94e7b558998..24f9d49c79b 100644 --- a/cmd/experimental/workspace_open_test.go +++ b/cmd/experimental/workspace_open_test.go @@ -67,7 +67,7 @@ func TestBuildWorkspaceURLFragmentBasedResources(t *testing.T) { func TestBuildWorkspaceURLUnknownResourceType(t *testing.T) { _, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "unknown", "123", "") assert.ErrorContains(t, err, "unknown resource type \"unknown\"") - assert.ErrorContains(t, err, "alerts, apps, catalogs, cluster_policies, clusters, dashboards, database_catalogs, database_instances, experiments, genie_spaces, instance_pools, jobs, mcp_services, model_services, model_serving_endpoints, models, notebooks, pipelines, postgres_catalogs, postgres_synced_tables, quality_monitors, queries, registered_models, schemas, secrets, synced_database_tables, vector_search_endpoints, vector_search_indexes, volumes, warehouses") + assert.ErrorContains(t, err, "alerts, apps, catalogs, cluster_policies, clusters, dashboards, database_catalogs, database_instances, experiments, genie_spaces, instance_pools, jobs, mcp_services, model_provider_services, model_services, model_serving_endpoints, models, notebooks, pipelines, postgres_catalogs, postgres_synced_tables, quality_monitors, queries, registered_models, schemas, secrets, synced_database_tables, vector_search_endpoints, vector_search_indexes, volumes, warehouses") } func TestBuildWorkspaceURLHostWithTrailingSlash(t *testing.T) { @@ -120,6 +120,7 @@ func TestWorkspaceOpenCommandCompletion(t *testing.T) { "instance_pools", "jobs", "mcp_services", + "model_provider_services", "model_services", "model_serving_endpoints", "models", @@ -151,7 +152,7 @@ func TestWorkspaceOpenCommandCompletionSecondArg(t *testing.T) { func TestWorkspaceOpenCommandHelpText(t *testing.T) { cmd := newWorkspaceOpenCommand() - assert.Contains(t, cmd.Long, "Supported resource types: alerts, apps, catalogs, cluster_policies, clusters, dashboards, database_catalogs, database_instances, experiments, genie_spaces, instance_pools, jobs, mcp_services, model_services, model_serving_endpoints, models, notebooks, pipelines, postgres_catalogs, postgres_synced_tables, quality_monitors, queries, registered_models, schemas, secrets, synced_database_tables, vector_search_endpoints, vector_search_indexes, volumes, warehouses.") + assert.Contains(t, cmd.Long, "Supported resource types: alerts, apps, catalogs, cluster_policies, clusters, dashboards, database_catalogs, database_instances, experiments, genie_spaces, instance_pools, jobs, mcp_services, model_provider_services, model_services, model_serving_endpoints, models, notebooks, pipelines, postgres_catalogs, postgres_synced_tables, quality_monitors, queries, registered_models, schemas, secrets, synced_database_tables, vector_search_endpoints, vector_search_indexes, volumes, warehouses.") assert.Contains(t, cmd.Long, "databricks experimental open jobs 123456789") assert.Contains(t, cmd.Long, "databricks experimental open notebooks /Users/user@example.com/my-notebook") assert.Contains(t, cmd.Long, "databricks experimental open registered_models catalog.schema.my_model") diff --git a/libs/testserver/fake_workspace.go b/libs/testserver/fake_workspace.go index 7786e7b1492..20543297b87 100644 --- a/libs/testserver/fake_workspace.go +++ b/libs/testserver/fake_workspace.go @@ -206,6 +206,7 @@ type FakeWorkspace struct { RegisteredModels map[string]catalog.RegisteredModelInfo ModelServices map[string]catalog.ModelService McpServices map[string]catalog.McpService + ModelProviderServices map[string]catalog.ModelProviderService ServingEndpoints map[string]serving.ServingEndpointDetailed VectorSearchEndpoints map[string]vectorsearch.EndpointInfo VectorSearchIndexes map[string]fakeVectorSearchIndex @@ -468,25 +469,26 @@ func NewFakeWorkspace(url, token string) *FakeWorkspace { files: make(map[string]FileEntry), repoIdByPath: make(map[string]int64), - Jobs: map[int64]jobs.Job{}, - JobRuns: map[int64]jobs.Run{}, - JobRunOutputs: map[int64]jobs.RunOutput{}, - JobRunIdempotency: map[string]int64{}, - Grants: map[string][]catalog.PrivilegeAssignment{}, - Pipelines: map[string]pipelines.GetPipelineResponse{}, - PipelineUpdates: map[string]bool{}, - Monitors: map[string]catalog.MonitorInfo{}, - Apps: map[string]apps.App{}, - Catalogs: map[string]catalog.CatalogInfo{}, - ExternalLocations: map[string]catalog.ExternalLocationInfo{}, - Schemas: map[string]catalog.SchemaInfo{}, - RegisteredModels: map[string]catalog.RegisteredModelInfo{}, - ModelServices: map[string]catalog.ModelService{}, - McpServices: map[string]catalog.McpService{}, - Volumes: map[string]catalog.VolumeInfo{}, - Dashboards: NewEventualMap[string, *fakeDashboard](strings.HasPrefix(token, EventualConsistencyTokenPrefix)), - PublishedDashboards: map[string]dashboards.PublishedDashboard{}, - GenieSpaces: map[string]dashboards.GenieSpace{}, + Jobs: map[int64]jobs.Job{}, + JobRuns: map[int64]jobs.Run{}, + JobRunOutputs: map[int64]jobs.RunOutput{}, + JobRunIdempotency: map[string]int64{}, + Grants: map[string][]catalog.PrivilegeAssignment{}, + Pipelines: map[string]pipelines.GetPipelineResponse{}, + PipelineUpdates: map[string]bool{}, + Monitors: map[string]catalog.MonitorInfo{}, + Apps: map[string]apps.App{}, + Catalogs: map[string]catalog.CatalogInfo{}, + ExternalLocations: map[string]catalog.ExternalLocationInfo{}, + Schemas: map[string]catalog.SchemaInfo{}, + RegisteredModels: map[string]catalog.RegisteredModelInfo{}, + ModelServices: map[string]catalog.ModelService{}, + McpServices: map[string]catalog.McpService{}, + ModelProviderServices: map[string]catalog.ModelProviderService{}, + Volumes: map[string]catalog.VolumeInfo{}, + Dashboards: NewEventualMap[string, *fakeDashboard](strings.HasPrefix(token, EventualConsistencyTokenPrefix)), + PublishedDashboards: map[string]dashboards.PublishedDashboard{}, + GenieSpaces: map[string]dashboards.GenieSpace{}, SqlWarehouses: map[string]sql.GetWarehouseResponse{ TestDefaultWarehouseId: { Id: TestDefaultWarehouseId, diff --git a/libs/testserver/handlers.go b/libs/testserver/handlers.go index be085e098e9..15aa89e9bdf 100644 --- a/libs/testserver/handlers.go +++ b/libs/testserver/handlers.go @@ -664,6 +664,24 @@ func AddDefaultHandlers(server *Server) { return MapDelete(req.Workspace, req.Workspace.McpServices, req.Vars["name"]) }) + // Model Provider Services (AI Gateway): + + server.Handle("POST", "/api/2.1/unity-catalog/model-provider-services", func(req Request) any { + return req.Workspace.ModelProviderServicesCreate(req) + }) + + server.Handle("GET", "/api/2.1/unity-catalog/model-provider-services/{name}", func(req Request) any { + return MapGet(req.Workspace, req.Workspace.ModelProviderServices, req.Vars["name"]) + }) + + server.Handle("PATCH", "/api/2.1/unity-catalog/model-provider-services/{name}", func(req Request) any { + return req.Workspace.ModelProviderServicesUpdate(req, req.Vars["name"]) + }) + + server.Handle("DELETE", "/api/2.1/unity-catalog/model-provider-services/{name}", func(req Request) any { + return MapDelete(req.Workspace, req.Workspace.ModelProviderServices, req.Vars["name"]) + }) + // Volumes: server.Handle("GET", "/api/2.1/unity-catalog/volumes/{full_name}", func(req Request) any { diff --git a/libs/testserver/model_provider_services.go b/libs/testserver/model_provider_services.go new file mode 100644 index 00000000000..622bf75888c --- /dev/null +++ b/libs/testserver/model_provider_services.go @@ -0,0 +1,70 @@ +package testserver + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/databricks/databricks-sdk-go/service/catalog" +) + +// ModelProviderServicesCreate fakes POST +// /api/2.1/unity-catalog/model-provider-services. See ModelServicesCreate for +// the shape: the ModelProviderService body is sent directly, parent and +// model_provider_service_id are query parameters, and the map is keyed by the +// {catalog}.{schema}.{model_provider_service} path segment. +func (s *FakeWorkspace) ModelProviderServicesCreate(req Request) Response { + defer s.LockUnlock()() + + var ms catalog.ModelProviderService + if err := json.Unmarshal(req.Body, &ms); err != nil { + return Response{ + Body: fmt.Sprintf("internal error: %s", err), + StatusCode: http.StatusInternalServerError, + } + } + + schema := strings.TrimPrefix(req.URL.Query().Get("parent"), "schemas/") + key := schema + "." + req.URL.Query().Get("model_provider_service_id") + + ms.Name = "model-provider-services/" + key + ms.CreatedBy = s.CurrentUser().UserName + ms.UpdatedBy = s.CurrentUser().UserName + ms.EffectiveOwner = s.CurrentUser().UserName + ms.MetastoreId = nextUUID() + + s.ModelProviderServices[key] = ms + return Response{ + Body: ms, + } +} + +func (s *FakeWorkspace) ModelProviderServicesUpdate(req Request, name string) Response { + defer s.LockUnlock()() + + existing, ok := s.ModelProviderServices[name] + if !ok { + return Response{ + StatusCode: http.StatusNotFound, + Body: fmt.Sprintf("model provider service %s not found", name), + } + } + + var incoming catalog.ModelProviderService + if err := json.Unmarshal(req.Body, &incoming); err != nil { + return Response{ + Body: fmt.Sprintf("internal error: %s", err), + StatusCode: http.StatusInternalServerError, + } + } + + existing.Comment = incoming.Comment + existing.Config = incoming.Config + existing.UpdatedBy = s.CurrentUser().UserName + + s.ModelProviderServices[name] = existing + return Response{ + Body: existing, + } +} diff --git a/libs/workspaceurls/urls.go b/libs/workspaceurls/urls.go index 3bbf1339de4..534bdc0fc83 100644 --- a/libs/workspaceurls/urls.go +++ b/libs/workspaceurls/urls.go @@ -24,6 +24,7 @@ var resourceURLPatterns = map[string]string{ "models": "ml/models/%s", "model_serving_endpoints": "ml/endpoints/%s", "model_services": "explore/data/model-services/%s", + "model_provider_services": "explore/data/model-provider-services/%s", "notebooks": "#notebook/%s", "pipelines": "pipelines/%s", "postgres_catalogs": "explore/data/%s", @@ -54,16 +55,17 @@ var resourceAliases = map[string]string{ // provided as a dot-separated name (e.g. "catalog.schema.model") but the URL // requires slash-separated segments. var dotSeparatedResources = map[string]bool{ - "catalogs": true, - "mcp_services": true, - "model_services": true, - "postgres_synced_tables": true, - "quality_monitors": true, - "registered_models": true, - "schemas": true, - "secrets": true, - "vector_search_indexes": true, - "volumes": true, + "catalogs": true, + "mcp_services": true, + "model_services": true, + "model_provider_services": true, + "postgres_synced_tables": true, + "quality_monitors": true, + "registered_models": true, + "schemas": true, + "secrets": true, + "vector_search_indexes": true, + "volumes": true, } // ResourceTypes returns a sorted list of all supported resource type names. diff --git a/python/databricks/bundles/core/__init__.py b/python/databricks/bundles/core/__init__.py index d66f21260ed..5d4d576ed47 100644 --- a/python/databricks/bundles/core/__init__.py +++ b/python/databricks/bundles/core/__init__.py @@ -31,6 +31,7 @@ "mcp_service_mutator", "mlflow_experiment_mutator", "mlflow_model_mutator", + "model_provider_service_mutator", "model_service_mutator", "model_serving_endpoint_mutator", "pipeline_mutator", @@ -68,6 +69,7 @@ mcp_service_mutator, mlflow_experiment_mutator, mlflow_model_mutator, + model_provider_service_mutator, model_service_mutator, model_serving_endpoint_mutator, pipeline_mutator, diff --git a/python/databricks/bundles/core/_generated/__init__.py b/python/databricks/bundles/core/_generated/__init__.py index 7b3c3bc985e..c9a140ab5f5 100644 --- a/python/databricks/bundles/core/_generated/__init__.py +++ b/python/databricks/bundles/core/_generated/__init__.py @@ -41,6 +41,10 @@ _McpServiceResources, mcp_service_mutator, ) +from databricks.bundles.core._generated.model_provider_services import ( + _ModelProviderServiceResources, + model_provider_service_mutator, +) from databricks.bundles.core._generated.model_services import ( _ModelServiceResources, model_service_mutator, @@ -112,6 +116,7 @@ "mcp_service_mutator", "mlflow_experiment_mutator", "mlflow_model_mutator", + "model_provider_service_mutator", "model_service_mutator", "model_serving_endpoint_mutator", "pipeline_mutator", @@ -142,6 +147,7 @@ class _GeneratedResources( _JobRunResources, _JobResources, _McpServiceResources, + _ModelProviderServiceResources, _ModelServiceResources, _ModelServingEndpointResources, _MlflowModelResources, @@ -175,6 +181,7 @@ def _all_resource_types() -> "tuple[_ResourceType, ...]": job_runs, jobs, mcp_services, + model_provider_services, model_services, model_serving_endpoints, models, @@ -205,6 +212,7 @@ def _all_resource_types() -> "tuple[_ResourceType, ...]": job_runs._resource_type(), jobs._resource_type(), mcp_services._resource_type(), + model_provider_services._resource_type(), model_services._resource_type(), model_serving_endpoints._resource_type(), models._resource_type(), diff --git a/python/databricks/bundles/core/_generated/model_provider_services.py b/python/databricks/bundles/core/_generated/model_provider_services.py new file mode 100644 index 00000000000..89f5b631fcc --- /dev/null +++ b/python/databricks/bundles/core/_generated/model_provider_services.py @@ -0,0 +1,130 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from collections.abc import Callable +from typing import TYPE_CHECKING, Optional, overload + +from databricks.bundles.core._bundle import Bundle +from databricks.bundles.core._location import Location +from databricks.bundles.core._resource_mutator import ResourceMutator +from databricks.bundles.core._transform import _transform + +if TYPE_CHECKING: + from databricks.bundles.core._resource_type import _ResourceType + from databricks.bundles.model_provider_services._models.model_provider_service import ( + ModelProviderService, + ModelProviderServiceParam, + ) + + +def _resource_type() -> "_ResourceType": + from databricks.bundles.core._resource_type import _ResourceType + from databricks.bundles.model_provider_services._models.model_provider_service import ( + ModelProviderService, + ) + + return _ResourceType( + resource_type=ModelProviderService, + singular_name="model_provider_service", + plural_name="model_provider_services", + ) + + +class _ModelProviderServiceResources: + """ + Generated model_provider_service accessors, mixed into Resources. + """ + + # Provided by the Resources subclass; declared here so the generated methods + # below type-check. + _resources: dict[str, dict] + + if TYPE_CHECKING: + + def add_location(self, path: tuple[str, ...], location: Location) -> None: ... + + def add_diagnostic_error( + self, + msg: str, + *, + detail: Optional[str] = None, + path: Optional[tuple[str, ...]] = None, + location: Optional[Location] = None, + ) -> None: ... + + @property + def model_provider_services(self) -> dict[str, "ModelProviderService"]: + return self._resources["model_provider_services"] + + def add_model_provider_service( + self, + resource_name: str, + model_provider_service: "ModelProviderServiceParam", + *, + location: Optional[Location] = None, + ) -> None: + """ + Adds the resource model_provider_service to the collection of resources. Resource name must be unique across all model_provider_services. + + :param resource_name: unique identifier for the model_provider_service + :param model_provider_service: the model_provider_service to add, can be ModelProviderService or dict + :param location: optional location of the model_provider_service in the source code + """ + from databricks.bundles.model_provider_services._models.model_provider_service import ( + ModelProviderService, + ) + + model_provider_service = _transform( + ModelProviderService, model_provider_service + ) + path = ("resources", "model_provider_services", resource_name) + location = location or Location.from_stack_frame(depth=1) + + if self._resources["model_provider_services"].get(resource_name): + self.add_diagnostic_error( + msg=f"Duplicate resource name '{resource_name}' for resource 'model_provider_service'. Resource names must be unique.", + location=location, + path=path, + ) + else: + if location: + self.add_location(path, location) + + self._resources["model_provider_services"][resource_name] = ( + model_provider_service + ) + + +@overload +def model_provider_service_mutator( + function: Callable[[Bundle, "ModelProviderService"], "ModelProviderService"], +) -> ResourceMutator["ModelProviderService"]: ... + + +@overload +def model_provider_service_mutator( + function: Callable[["ModelProviderService"], "ModelProviderService"], +) -> ResourceMutator["ModelProviderService"]: ... + + +def model_provider_service_mutator( + function: Callable, +) -> ResourceMutator["ModelProviderService"]: + """ + Decorator for defining mutator for model_provider_services. Function should return a new instance of the model_provider_service + with the desired changes, instead of mutating the input model_provider_service. + + Example: + + .. code-block:: python + + @model_provider_service_mutator + def my_model_provider_service_mutator(bundle: Bundle, model_provider_service: ModelProviderService) -> ModelProviderService: + return replace(model_provider_service, ...) + + :param function: Function that mutates model_provider_services. + """ + from databricks.bundles.model_provider_services._models.model_provider_service import ( + ModelProviderService, + ) + + return ResourceMutator(resource_type=ModelProviderService, function=function) diff --git a/python/databricks/bundles/model_provider_services/__init__.py b/python/databricks/bundles/model_provider_services/__init__.py new file mode 100644 index 00000000000..17a2fb82470 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/__init__.py @@ -0,0 +1,224 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +__all__ = [ + "InferenceTableConfig", + "InferenceTableConfigDict", + "InferenceTableConfigParam", + "Lifecycle", + "LifecycleDict", + "LifecycleParam", + "ModelProviderService", + "ModelProviderServiceConfig", + "ModelProviderServiceConfigAmazonBedrockProviderConfig", + "ModelProviderServiceConfigAmazonBedrockProviderConfigDict", + "ModelProviderServiceConfigAmazonBedrockProviderConfigParam", + "ModelProviderServiceConfigAmazonBedrockProviderDirectConfig", + "ModelProviderServiceConfigAmazonBedrockProviderDirectConfigDict", + "ModelProviderServiceConfigAmazonBedrockProviderDirectConfigParam", + "ModelProviderServiceConfigAnthropicProviderConfig", + "ModelProviderServiceConfigAnthropicProviderConfigDict", + "ModelProviderServiceConfigAnthropicProviderConfigParam", + "ModelProviderServiceConfigAnthropicProviderDirectConfig", + "ModelProviderServiceConfigAnthropicProviderDirectConfigDict", + "ModelProviderServiceConfigAnthropicProviderDirectConfigParam", + "ModelProviderServiceConfigAnthropicProviderRelayedConfig", + "ModelProviderServiceConfigAnthropicProviderRelayedConfigDict", + "ModelProviderServiceConfigAnthropicProviderRelayedConfigParam", + "ModelProviderServiceConfigAwsAccessKey", + "ModelProviderServiceConfigAwsAccessKeyDict", + "ModelProviderServiceConfigAwsAccessKeyParam", + "ModelProviderServiceConfigAzureOpenAiProviderConfig", + "ModelProviderServiceConfigAzureOpenAiProviderConfigDict", + "ModelProviderServiceConfigAzureOpenAiProviderConfigParam", + "ModelProviderServiceConfigAzureOpenAiProviderDirectConfig", + "ModelProviderServiceConfigAzureOpenAiProviderDirectConfigDict", + "ModelProviderServiceConfigAzureOpenAiProviderDirectConfigParam", + "ModelProviderServiceConfigCustomProviderConfig", + "ModelProviderServiceConfigCustomProviderConfigDict", + "ModelProviderServiceConfigCustomProviderConfigParam", + "ModelProviderServiceConfigCustomProviderDirectConfig", + "ModelProviderServiceConfigCustomProviderDirectConfigDict", + "ModelProviderServiceConfigCustomProviderDirectConfigParam", + "ModelProviderServiceConfigDict", + "ModelProviderServiceConfigEntraServicePrincipal", + "ModelProviderServiceConfigEntraServicePrincipalDict", + "ModelProviderServiceConfigEntraServicePrincipalParam", + "ModelProviderServiceConfigExternalModelProviderType", + "ModelProviderServiceConfigExternalModelProviderTypeParam", + "ModelProviderServiceConfigGeminiEnterpriseProviderConfig", + "ModelProviderServiceConfigGeminiEnterpriseProviderConfigDict", + "ModelProviderServiceConfigGeminiEnterpriseProviderConfigParam", + "ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig", + "ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigDict", + "ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigParam", + "ModelProviderServiceConfigMicrosoftFoundryProviderConfig", + "ModelProviderServiceConfigMicrosoftFoundryProviderConfigDict", + "ModelProviderServiceConfigMicrosoftFoundryProviderConfigParam", + "ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig", + "ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigDict", + "ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigParam", + "ModelProviderServiceConfigModelTargetConfig", + "ModelProviderServiceConfigModelTargetConfigDict", + "ModelProviderServiceConfigModelTargetConfigParam", + "ModelProviderServiceConfigOpenAiProviderConfig", + "ModelProviderServiceConfigOpenAiProviderConfigDict", + "ModelProviderServiceConfigOpenAiProviderConfigParam", + "ModelProviderServiceConfigOpenAiProviderDirectConfig", + "ModelProviderServiceConfigOpenAiProviderDirectConfigDict", + "ModelProviderServiceConfigOpenAiProviderDirectConfigParam", + "ModelProviderServiceConfigParam", + "ModelProviderServiceConfigProviderSecret", + "ModelProviderServiceConfigProviderSecretDict", + "ModelProviderServiceConfigProviderSecretParam", + "ModelProviderServiceConfigServiceCredential", + "ModelProviderServiceConfigServiceCredentialDict", + "ModelProviderServiceConfigServiceCredentialParam", + "ModelProviderServiceDict", + "ModelProviderServiceParam", + "RateLimit", + "RateLimitDict", + "RateLimitParam", + "RateLimitRateLimitKey", + "RateLimitRateLimitKeyParam", + "RateLimitRateLimitRenewalPeriod", + "RateLimitRateLimitRenewalPeriodParam", +] + + +from databricks.bundles.model_provider_services._models.inference_table_config import ( + InferenceTableConfig, + InferenceTableConfigDict, + InferenceTableConfigParam, +) +from databricks.bundles.model_provider_services._models.lifecycle import ( + Lifecycle, + LifecycleDict, + LifecycleParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service import ( + ModelProviderService, + ModelProviderServiceDict, + ModelProviderServiceParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config import ( + ModelProviderServiceConfig, + ModelProviderServiceConfigDict, + ModelProviderServiceConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_amazon_bedrock_provider_config import ( + ModelProviderServiceConfigAmazonBedrockProviderConfig, + ModelProviderServiceConfigAmazonBedrockProviderConfigDict, + ModelProviderServiceConfigAmazonBedrockProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_amazon_bedrock_provider_direct_config import ( + ModelProviderServiceConfigAmazonBedrockProviderDirectConfig, + ModelProviderServiceConfigAmazonBedrockProviderDirectConfigDict, + ModelProviderServiceConfigAmazonBedrockProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_anthropic_provider_config import ( + ModelProviderServiceConfigAnthropicProviderConfig, + ModelProviderServiceConfigAnthropicProviderConfigDict, + ModelProviderServiceConfigAnthropicProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_anthropic_provider_direct_config import ( + ModelProviderServiceConfigAnthropicProviderDirectConfig, + ModelProviderServiceConfigAnthropicProviderDirectConfigDict, + ModelProviderServiceConfigAnthropicProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_anthropic_provider_relayed_config import ( + ModelProviderServiceConfigAnthropicProviderRelayedConfig, + ModelProviderServiceConfigAnthropicProviderRelayedConfigDict, + ModelProviderServiceConfigAnthropicProviderRelayedConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_aws_access_key import ( + ModelProviderServiceConfigAwsAccessKey, + ModelProviderServiceConfigAwsAccessKeyDict, + ModelProviderServiceConfigAwsAccessKeyParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_azure_open_ai_provider_config import ( + ModelProviderServiceConfigAzureOpenAiProviderConfig, + ModelProviderServiceConfigAzureOpenAiProviderConfigDict, + ModelProviderServiceConfigAzureOpenAiProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_azure_open_ai_provider_direct_config import ( + ModelProviderServiceConfigAzureOpenAiProviderDirectConfig, + ModelProviderServiceConfigAzureOpenAiProviderDirectConfigDict, + ModelProviderServiceConfigAzureOpenAiProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_custom_provider_config import ( + ModelProviderServiceConfigCustomProviderConfig, + ModelProviderServiceConfigCustomProviderConfigDict, + ModelProviderServiceConfigCustomProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_custom_provider_direct_config import ( + ModelProviderServiceConfigCustomProviderDirectConfig, + ModelProviderServiceConfigCustomProviderDirectConfigDict, + ModelProviderServiceConfigCustomProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_entra_service_principal import ( + ModelProviderServiceConfigEntraServicePrincipal, + ModelProviderServiceConfigEntraServicePrincipalDict, + ModelProviderServiceConfigEntraServicePrincipalParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_external_model_provider_type import ( + ModelProviderServiceConfigExternalModelProviderType, + ModelProviderServiceConfigExternalModelProviderTypeParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_gemini_enterprise_provider_config import ( + ModelProviderServiceConfigGeminiEnterpriseProviderConfig, + ModelProviderServiceConfigGeminiEnterpriseProviderConfigDict, + ModelProviderServiceConfigGeminiEnterpriseProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_gemini_enterprise_provider_direct_config import ( + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig, + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigDict, + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_microsoft_foundry_provider_config import ( + ModelProviderServiceConfigMicrosoftFoundryProviderConfig, + ModelProviderServiceConfigMicrosoftFoundryProviderConfigDict, + ModelProviderServiceConfigMicrosoftFoundryProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_microsoft_foundry_provider_direct_config import ( + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig, + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigDict, + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_model_target_config import ( + ModelProviderServiceConfigModelTargetConfig, + ModelProviderServiceConfigModelTargetConfigDict, + ModelProviderServiceConfigModelTargetConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_open_ai_provider_config import ( + ModelProviderServiceConfigOpenAiProviderConfig, + ModelProviderServiceConfigOpenAiProviderConfigDict, + ModelProviderServiceConfigOpenAiProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_open_ai_provider_direct_config import ( + ModelProviderServiceConfigOpenAiProviderDirectConfig, + ModelProviderServiceConfigOpenAiProviderDirectConfigDict, + ModelProviderServiceConfigOpenAiProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretDict, + ModelProviderServiceConfigProviderSecretParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_service_credential import ( + ModelProviderServiceConfigServiceCredential, + ModelProviderServiceConfigServiceCredentialDict, + ModelProviderServiceConfigServiceCredentialParam, +) +from databricks.bundles.model_provider_services._models.rate_limit import ( + RateLimit, + RateLimitDict, + RateLimitParam, +) +from databricks.bundles.model_provider_services._models.rate_limit_rate_limit_key import ( + RateLimitRateLimitKey, + RateLimitRateLimitKeyParam, +) +from databricks.bundles.model_provider_services._models.rate_limit_rate_limit_renewal_period import ( + RateLimitRateLimitRenewalPeriod, + RateLimitRateLimitRenewalPeriodParam, +) diff --git a/python/databricks/bundles/model_provider_services/_models/inference_table_config.py b/python/databricks/bundles/model_provider_services/_models/inference_table_config.py new file mode 100644 index 00000000000..45165416e17 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/inference_table_config.py @@ -0,0 +1,66 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr, VariableOrOptional + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class InferenceTableConfig: + """ + Configuration for logging request and response payloads to a Unity Catalog + inference table. When this configuration is present, payload logging is + enabled by default. + """ + + parent: VariableOr[str] + """ + Parent Unity Catalog schema where the inference table is created, in the + form `schemas/{catalog}.{schema}`. Required when configuring an inference + table. After the inference table is created, this field cannot be changed. + """ + + table_name_prefix: VariableOrOptional[str] = None + """ + Prefix used to form the inference table's registered name. AI Gateway + appends `_payload`; for example, `table_name_prefix = "orders"` creates + `orders_payload`. If unset, the prefix defaults to the service name. Read + `table` from the response for the resulting resource name. After the + inference table is created, this field cannot be changed. + """ + + @classmethod + def from_dict(cls, value: "InferenceTableConfigDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "InferenceTableConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class InferenceTableConfigDict(TypedDict, total=False): + """""" + + parent: VariableOr[str] + """ + Parent Unity Catalog schema where the inference table is created, in the + form `schemas/{catalog}.{schema}`. Required when configuring an inference + table. After the inference table is created, this field cannot be changed. + """ + + table_name_prefix: VariableOrOptional[str] + """ + Prefix used to form the inference table's registered name. AI Gateway + appends `_payload`; for example, `table_name_prefix = "orders"` creates + `orders_payload`. If unset, the prefix defaults to the service name. Read + `table` from the response for the resulting resource name. After the + inference table is created, this field cannot be changed. + """ + + +InferenceTableConfigParam = InferenceTableConfigDict | InferenceTableConfig diff --git a/python/databricks/bundles/model_provider_services/_models/lifecycle.py b/python/databricks/bundles/model_provider_services/_models/lifecycle.py new file mode 100644 index 00000000000..697776a198e --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/lifecycle.py @@ -0,0 +1,40 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class Lifecycle: + """""" + + prevent_destroy: VariableOrOptional[bool] = None + """ + Lifecycle setting to prevent the resource from being destroyed. + """ + + @classmethod + def from_dict(cls, value: "LifecycleDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "LifecycleDict": + return _transform_to_json_value(self) # type:ignore + + +class LifecycleDict(TypedDict, total=False): + """""" + + prevent_destroy: VariableOrOptional[bool] + """ + Lifecycle setting to prevent the resource from being destroyed. + """ + + +LifecycleParam = LifecycleDict | Lifecycle diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service.py new file mode 100644 index 00000000000..65fcb48e421 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service.py @@ -0,0 +1,59 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._resource import Resource +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr, VariableOrOptional +from databricks.bundles.model_provider_services._models.lifecycle import ( + Lifecycle, + LifecycleParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config import ( + ModelProviderServiceConfig, + ModelProviderServiceConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderService(Resource): + """""" + + model_provider_service_id: VariableOr[str] + + parent: VariableOr[str] + + comment: VariableOrOptional[str] = None + + config: VariableOrOptional[ModelProviderServiceConfig] = None + + lifecycle: VariableOrOptional[Lifecycle] = None + + @classmethod + def from_dict(cls, value: "ModelProviderServiceDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceDict(TypedDict, total=False): + """""" + + model_provider_service_id: VariableOr[str] + + parent: VariableOr[str] + + comment: VariableOrOptional[str] + + config: VariableOrOptional[ModelProviderServiceConfigParam] + + lifecycle: VariableOrOptional[LifecycleParam] + + +ModelProviderServiceParam = ModelProviderServiceDict | ModelProviderService diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config.py new file mode 100644 index 00000000000..644105ae253 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config.py @@ -0,0 +1,304 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrList, VariableOrOptional +from databricks.bundles.model_provider_services._models.inference_table_config import ( + InferenceTableConfig, + InferenceTableConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_amazon_bedrock_provider_config import ( + ModelProviderServiceConfigAmazonBedrockProviderConfig, + ModelProviderServiceConfigAmazonBedrockProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_anthropic_provider_config import ( + ModelProviderServiceConfigAnthropicProviderConfig, + ModelProviderServiceConfigAnthropicProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_azure_open_ai_provider_config import ( + ModelProviderServiceConfigAzureOpenAiProviderConfig, + ModelProviderServiceConfigAzureOpenAiProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_custom_provider_config import ( + ModelProviderServiceConfigCustomProviderConfig, + ModelProviderServiceConfigCustomProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_external_model_provider_type import ( + ModelProviderServiceConfigExternalModelProviderType, + ModelProviderServiceConfigExternalModelProviderTypeParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_gemini_enterprise_provider_config import ( + ModelProviderServiceConfigGeminiEnterpriseProviderConfig, + ModelProviderServiceConfigGeminiEnterpriseProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_microsoft_foundry_provider_config import ( + ModelProviderServiceConfigMicrosoftFoundryProviderConfig, + ModelProviderServiceConfigMicrosoftFoundryProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_model_target_config import ( + ModelProviderServiceConfigModelTargetConfig, + ModelProviderServiceConfigModelTargetConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_open_ai_provider_config import ( + ModelProviderServiceConfigOpenAiProviderConfig, + ModelProviderServiceConfigOpenAiProviderConfigParam, +) +from databricks.bundles.model_provider_services._models.rate_limit import ( + RateLimit, + RateLimitParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfig: + """ + Behavioral configuration for a ModelProviderService: provider authentication + and provider-specific fields, the catalog of models this provider + service can route to, and the passthrough policy that governs how request + headers, query parameters, and unmanaged subpaths cross the trust boundary + to the upstream provider. + """ + + allow_all_targets: VariableOrOptional[bool] = None + """ + When true, accepts any model exposed by the upstream provider; `targets` + is not required and does not restrict routability. When false, only + models listed in `targets` are routable. Defaults to false. + """ + + amazon_bedrock: VariableOrOptional[ + ModelProviderServiceConfigAmazonBedrockProviderConfig + ] = None + """ + Amazon Bedrock provider configuration. + """ + + anthropic: VariableOrOptional[ModelProviderServiceConfigAnthropicProviderConfig] = ( + None + ) + """ + Anthropic provider configuration. Exactly one of `direct` or `relayed` must + be set on Create; the two are mutually exclusive. + """ + + azure_openai: VariableOrOptional[ + ModelProviderServiceConfigAzureOpenAiProviderConfig + ] = None + """ + Azure OpenAI provider configuration. + """ + + custom: VariableOrOptional[ModelProviderServiceConfigCustomProviderConfig] = None + """ + Custom OpenAI-compatible provider configuration with bearer-token + authentication. + """ + + forward_headers: VariableOrOptional[bool] = None + """ + Whether to forward incoming HTTP headers to the upstream provider. Defaults + to false and is configured for the entire provider service, not per request. + Upstream authentication is configured separately in the provider-specific + configuration. + """ + + forward_query_parameters: VariableOrOptional[bool] = None + """ + Whether to forward incoming query parameters to the upstream provider. + Defaults to false and is configured for the entire provider service, not + per request. + """ + + forward_unmanaged_paths: VariableOrOptional[bool] = None + """ + Whether to proxy paths that AI Gateway does not recognize as configured + provider-native API types. Defaults to false. When true, these paths are + forwarded unchanged to the upstream provider. When false, only + recognized API paths are served. Enabling this broadens the upstream API + surface exposed through the provider service. + """ + + gemini_enterprise: VariableOrOptional[ + ModelProviderServiceConfigGeminiEnterpriseProviderConfig + ] = None + """ + Gemini Enterprise provider configuration. + """ + + inference_table: VariableOrOptional[InferenceTableConfig] = None + """ + Payload logging configuration for requests sent directly to this provider + service. Requests routed through a model service are captured by that model + service's inference table instead. + """ + + microsoft_foundry: VariableOrOptional[ + ModelProviderServiceConfigMicrosoftFoundryProviderConfig + ] = None + """ + Microsoft Foundry provider configuration. + """ + + openai: VariableOrOptional[ModelProviderServiceConfigOpenAiProviderConfig] = None + """ + OpenAI provider configuration. + """ + + provider_type: VariableOrOptional[ + ModelProviderServiceConfigExternalModelProviderType + ] = None + """ + External model provider. Required on Create and immutable thereafter. Set + the matching provider-specific configuration, such as `openai`, + `azure_openai`, or `amazon_bedrock`. + """ + + rate_limits: VariableOrList[RateLimit] = field(default_factory=list) + """ + Rate limits for requests sent directly to this provider service. Requests + routed through a model service use that model service's rate limits instead. + """ + + targets: VariableOrList[ModelProviderServiceConfigModelTargetConfig] = field( + default_factory=list + ) + """ + Models and provider-native API types exposed by this provider service. Each + entry must include at least one `native_api_types` value. When + `allow_all_targets` is false, at least one entry is required and model + service destinations can reference only listed models. When + `allow_all_targets` is true, any upstream model is routable; entries in + this list provide API-type metadata without restricting other models. + """ + + @classmethod + def from_dict(cls, value: "ModelProviderServiceConfigDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigDict(TypedDict, total=False): + """""" + + allow_all_targets: VariableOrOptional[bool] + """ + When true, accepts any model exposed by the upstream provider; `targets` + is not required and does not restrict routability. When false, only + models listed in `targets` are routable. Defaults to false. + """ + + amazon_bedrock: VariableOrOptional[ + ModelProviderServiceConfigAmazonBedrockProviderConfigParam + ] + """ + Amazon Bedrock provider configuration. + """ + + anthropic: VariableOrOptional[ + ModelProviderServiceConfigAnthropicProviderConfigParam + ] + """ + Anthropic provider configuration. Exactly one of `direct` or `relayed` must + be set on Create; the two are mutually exclusive. + """ + + azure_openai: VariableOrOptional[ + ModelProviderServiceConfigAzureOpenAiProviderConfigParam + ] + """ + Azure OpenAI provider configuration. + """ + + custom: VariableOrOptional[ModelProviderServiceConfigCustomProviderConfigParam] + """ + Custom OpenAI-compatible provider configuration with bearer-token + authentication. + """ + + forward_headers: VariableOrOptional[bool] + """ + Whether to forward incoming HTTP headers to the upstream provider. Defaults + to false and is configured for the entire provider service, not per request. + Upstream authentication is configured separately in the provider-specific + configuration. + """ + + forward_query_parameters: VariableOrOptional[bool] + """ + Whether to forward incoming query parameters to the upstream provider. + Defaults to false and is configured for the entire provider service, not + per request. + """ + + forward_unmanaged_paths: VariableOrOptional[bool] + """ + Whether to proxy paths that AI Gateway does not recognize as configured + provider-native API types. Defaults to false. When true, these paths are + forwarded unchanged to the upstream provider. When false, only + recognized API paths are served. Enabling this broadens the upstream API + surface exposed through the provider service. + """ + + gemini_enterprise: VariableOrOptional[ + ModelProviderServiceConfigGeminiEnterpriseProviderConfigParam + ] + """ + Gemini Enterprise provider configuration. + """ + + inference_table: VariableOrOptional[InferenceTableConfigParam] + """ + Payload logging configuration for requests sent directly to this provider + service. Requests routed through a model service are captured by that model + service's inference table instead. + """ + + microsoft_foundry: VariableOrOptional[ + ModelProviderServiceConfigMicrosoftFoundryProviderConfigParam + ] + """ + Microsoft Foundry provider configuration. + """ + + openai: VariableOrOptional[ModelProviderServiceConfigOpenAiProviderConfigParam] + """ + OpenAI provider configuration. + """ + + provider_type: VariableOrOptional[ + ModelProviderServiceConfigExternalModelProviderTypeParam + ] + """ + External model provider. Required on Create and immutable thereafter. Set + the matching provider-specific configuration, such as `openai`, + `azure_openai`, or `amazon_bedrock`. + """ + + rate_limits: VariableOrList[RateLimitParam] + """ + Rate limits for requests sent directly to this provider service. Requests + routed through a model service use that model service's rate limits instead. + """ + + targets: VariableOrList[ModelProviderServiceConfigModelTargetConfigParam] + """ + Models and provider-native API types exposed by this provider service. Each + entry must include at least one `native_api_types` value. When + `allow_all_targets` is false, at least one entry is required and model + service destinations can reference only listed models. When + `allow_all_targets` is true, any upstream model is routable; entries in + this list provide API-type metadata without restricting other models. + """ + + +ModelProviderServiceConfigParam = ( + ModelProviderServiceConfigDict | ModelProviderServiceConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_amazon_bedrock_provider_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_amazon_bedrock_provider_config.py new file mode 100644 index 00000000000..cf6015f13c5 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_amazon_bedrock_provider_config.py @@ -0,0 +1,55 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_amazon_bedrock_provider_direct_config import ( + ModelProviderServiceConfigAmazonBedrockProviderDirectConfig, + ModelProviderServiceConfigAmazonBedrockProviderDirectConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAmazonBedrockProviderConfig: + """ + Amazon Bedrock provider configuration. + """ + + direct: VariableOrOptional[ + ModelProviderServiceConfigAmazonBedrockProviderDirectConfig + ] = None + """ + Amazon Bedrock region and authentication configuration. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigAmazonBedrockProviderConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigAmazonBedrockProviderConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAmazonBedrockProviderConfigDict(TypedDict, total=False): + """""" + + direct: VariableOrOptional[ + ModelProviderServiceConfigAmazonBedrockProviderDirectConfigParam + ] + """ + Amazon Bedrock region and authentication configuration. + """ + + +ModelProviderServiceConfigAmazonBedrockProviderConfigParam = ( + ModelProviderServiceConfigAmazonBedrockProviderConfigDict + | ModelProviderServiceConfigAmazonBedrockProviderConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_amazon_bedrock_provider_direct_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_amazon_bedrock_provider_direct_config.py new file mode 100644 index 00000000000..911338ad79e --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_amazon_bedrock_provider_direct_config.py @@ -0,0 +1,108 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_aws_access_key import ( + ModelProviderServiceConfigAwsAccessKey, + ModelProviderServiceConfigAwsAccessKeyParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_service_credential import ( + ModelProviderServiceConfigServiceCredential, + ModelProviderServiceConfigServiceCredentialParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAmazonBedrockProviderDirectConfig: + """ + Direct form of Amazon Bedrock provider config. + + Authentication is one of two mutually exclusive modes, exactly one of which + must be supplied on Create: + - Access keys: set `aws_access_key`, leave `service_credential` unset. + - Unity Catalog service credential: set `service_credential.name` to the + resource name `credentials/{name}`, leave `aws_access_key` unset. The + credential value lives in Unity Catalog and is referenced by name, not + held on this message. + Setting more than one mode is rejected. + """ + + aws_access_key: VariableOrOptional[ModelProviderServiceConfigAwsAccessKey] = None + """ + AWS access-key-pair authentication. Set `access_key_id` and + `secret_access_key.plaintext`. Mutually exclusive with + `service_credential`. + """ + + region: VariableOrOptional[str] = None + """ + AWS region where the Bedrock endpoint is hosted (e.g., `us-east-1`). + Required on Create. + """ + + service_credential: VariableOrOptional[ + ModelProviderServiceConfigServiceCredential + ] = None + """ + Reference to a Unity Catalog service credential authorizing Bedrock + requests. On Create, supply `service_credential.name` in the form + `credentials/{name}`. Required on Create when using service-credential + authentication; mutually exclusive with `aws_access_key`. The credential + is referenced by name; its value is not carried here. Only + supported on AWS-hosted workspaces. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigAmazonBedrockProviderDirectConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict( + self, + ) -> "ModelProviderServiceConfigAmazonBedrockProviderDirectConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAmazonBedrockProviderDirectConfigDict( + TypedDict, total=False +): + """""" + + aws_access_key: VariableOrOptional[ModelProviderServiceConfigAwsAccessKeyParam] + """ + AWS access-key-pair authentication. Set `access_key_id` and + `secret_access_key.plaintext`. Mutually exclusive with + `service_credential`. + """ + + region: VariableOrOptional[str] + """ + AWS region where the Bedrock endpoint is hosted (e.g., `us-east-1`). + Required on Create. + """ + + service_credential: VariableOrOptional[ + ModelProviderServiceConfigServiceCredentialParam + ] + """ + Reference to a Unity Catalog service credential authorizing Bedrock + requests. On Create, supply `service_credential.name` in the form + `credentials/{name}`. Required on Create when using service-credential + authentication; mutually exclusive with `aws_access_key`. The credential + is referenced by name; its value is not carried here. Only + supported on AWS-hosted workspaces. + """ + + +ModelProviderServiceConfigAmazonBedrockProviderDirectConfigParam = ( + ModelProviderServiceConfigAmazonBedrockProviderDirectConfigDict + | ModelProviderServiceConfigAmazonBedrockProviderDirectConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_config.py new file mode 100644 index 00000000000..c7b3311ca93 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_config.py @@ -0,0 +1,80 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_anthropic_provider_direct_config import ( + ModelProviderServiceConfigAnthropicProviderDirectConfig, + ModelProviderServiceConfigAnthropicProviderDirectConfigParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_anthropic_provider_relayed_config import ( + ModelProviderServiceConfigAnthropicProviderRelayedConfig, + ModelProviderServiceConfigAnthropicProviderRelayedConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAnthropicProviderConfig: + """ + Anthropic provider configuration. Exactly one of `direct` or `relayed` must + be set on Create; the two are mutually exclusive. + """ + + direct: VariableOrOptional[ + ModelProviderServiceConfigAnthropicProviderDirectConfig + ] = None + """ + Direct authentication with an API key supplied in + `direct.api_key.plaintext`. Required unless `relayed` is set. + """ + + relayed: VariableOrOptional[ + ModelProviderServiceConfigAnthropicProviderRelayedConfig + ] = None + """ + Relayed authentication. Each inference request supplies the caller's + OAuth token, which is forwarded to Anthropic. No Anthropic credential is + stored. Mutually exclusive with `direct`. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigAnthropicProviderConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigAnthropicProviderConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAnthropicProviderConfigDict(TypedDict, total=False): + """""" + + direct: VariableOrOptional[ + ModelProviderServiceConfigAnthropicProviderDirectConfigParam + ] + """ + Direct authentication with an API key supplied in + `direct.api_key.plaintext`. Required unless `relayed` is set. + """ + + relayed: VariableOrOptional[ + ModelProviderServiceConfigAnthropicProviderRelayedConfigParam + ] + """ + Relayed authentication. Each inference request supplies the caller's + OAuth token, which is forwarded to Anthropic. No Anthropic credential is + stored. Mutually exclusive with `direct`. + """ + + +ModelProviderServiceConfigAnthropicProviderConfigParam = ( + ModelProviderServiceConfigAnthropicProviderConfigDict + | ModelProviderServiceConfigAnthropicProviderConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_direct_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_direct_config.py new file mode 100644 index 00000000000..d03a79d596d --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_direct_config.py @@ -0,0 +1,55 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAnthropicProviderDirectConfig: + """ + Direct form of Anthropic provider config. + """ + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = None + """ + Anthropic API key. Required when creating the service. Supply the value + in `api_key.plaintext`. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigAnthropicProviderDirectConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigAnthropicProviderDirectConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAnthropicProviderDirectConfigDict( + TypedDict, total=False +): + """""" + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + Anthropic API key. Required when creating the service. Supply the value + in `api_key.plaintext`. + """ + + +ModelProviderServiceConfigAnthropicProviderDirectConfigParam = ( + ModelProviderServiceConfigAnthropicProviderDirectConfigDict + | ModelProviderServiceConfigAnthropicProviderDirectConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_relayed_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_relayed_config.py new file mode 100644 index 00000000000..7754ff58862 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_anthropic_provider_relayed_config.py @@ -0,0 +1,40 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAnthropicProviderRelayedConfig: + """ + Relayed Anthropic provider configuration. Each inference request supplies + the caller's OAuth token, which is forwarded to Anthropic. No Anthropic + credential is stored. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigAnthropicProviderRelayedConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigAnthropicProviderRelayedConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAnthropicProviderRelayedConfigDict( + TypedDict, total=False +): + """""" + + +ModelProviderServiceConfigAnthropicProviderRelayedConfigParam = ( + ModelProviderServiceConfigAnthropicProviderRelayedConfigDict + | ModelProviderServiceConfigAnthropicProviderRelayedConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_aws_access_key.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_aws_access_key.py new file mode 100644 index 00000000000..6c57b6912d8 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_aws_access_key.py @@ -0,0 +1,68 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAwsAccessKey: + """ + AWS access-key-pair auth for Amazon Bedrock: a SigV4-signing key pair. + """ + + access_key_id: VariableOrOptional[str] = None + """ + AWS access key ID. Required on Create when using access-key auth. Treated as + username-equivalent (not a secret value): round-trips on reads and is + scrubbed from audit logs. + """ + + secret_access_key: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = ( + None + ) + """ + AWS secret access key paired with `access_key_id`. Required when creating + a service with access-key authentication. Supply the value in + `secret_access_key.plaintext`. + """ + + @classmethod + def from_dict(cls, value: "ModelProviderServiceConfigAwsAccessKeyDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigAwsAccessKeyDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAwsAccessKeyDict(TypedDict, total=False): + """""" + + access_key_id: VariableOrOptional[str] + """ + AWS access key ID. Required on Create when using access-key auth. Treated as + username-equivalent (not a secret value): round-trips on reads and is + scrubbed from audit logs. + """ + + secret_access_key: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + AWS secret access key paired with `access_key_id`. Required when creating + a service with access-key authentication. Supply the value in + `secret_access_key.plaintext`. + """ + + +ModelProviderServiceConfigAwsAccessKeyParam = ( + ModelProviderServiceConfigAwsAccessKeyDict | ModelProviderServiceConfigAwsAccessKey +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_azure_open_ai_provider_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_azure_open_ai_provider_config.py new file mode 100644 index 00000000000..60726eeffed --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_azure_open_ai_provider_config.py @@ -0,0 +1,55 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_azure_open_ai_provider_direct_config import ( + ModelProviderServiceConfigAzureOpenAiProviderDirectConfig, + ModelProviderServiceConfigAzureOpenAiProviderDirectConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAzureOpenAiProviderConfig: + """ + Azure OpenAI provider configuration. + """ + + direct: VariableOrOptional[ + ModelProviderServiceConfigAzureOpenAiProviderDirectConfig + ] = None + """ + Azure OpenAI endpoint and authentication configuration. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigAzureOpenAiProviderConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigAzureOpenAiProviderConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAzureOpenAiProviderConfigDict(TypedDict, total=False): + """""" + + direct: VariableOrOptional[ + ModelProviderServiceConfigAzureOpenAiProviderDirectConfigParam + ] + """ + Azure OpenAI endpoint and authentication configuration. + """ + + +ModelProviderServiceConfigAzureOpenAiProviderConfigParam = ( + ModelProviderServiceConfigAzureOpenAiProviderConfigDict + | ModelProviderServiceConfigAzureOpenAiProviderConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_azure_open_ai_provider_direct_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_azure_open_ai_provider_direct_config.py new file mode 100644 index 00000000000..9ba84e99974 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_azure_open_ai_provider_direct_config.py @@ -0,0 +1,132 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_entra_service_principal import ( + ModelProviderServiceConfigEntraServicePrincipal, + ModelProviderServiceConfigEntraServicePrincipalParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_service_credential import ( + ModelProviderServiceConfigServiceCredential, + ModelProviderServiceConfigServiceCredentialParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigAzureOpenAiProviderDirectConfig: + """ + Direct form of Azure OpenAI provider config. Exactly one of three + mutually-exclusive auth modes must be supplied on Create: + - API key: set `api_key`, leave `entra_service_principal` and + `service_credential` unset. + - Entra ID (service principal): set `entra_service_principal`, leave + `api_key` and `service_credential` unset. + - Unity Catalog service credential: set `service_credential.name` to the + resource name `credentials/{name}`, leave `api_key` and + `entra_service_principal` unset. The credential value lives in Unity + Catalog and is referenced by name, not held on this message. Only + supported on Azure-hosted workspaces. + Setting more than one mode is rejected. + """ + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = None + """ + Azure OpenAI API key. Supply the value in `api_key.plaintext`. Mutually + exclusive with Entra ID and Unity Catalog service credential + authentication. + """ + + base_url: VariableOrOptional[str] = None + """ + Full Azure OpenAI endpoint base URL, e.g. + `https://myresource.openai.azure.com`. Required on Create. + """ + + entra_service_principal: VariableOrOptional[ + ModelProviderServiceConfigEntraServicePrincipal + ] = None + """ + Entra ID service-principal authentication. Set `tenant_id`, `client_id`, + and `client_secret.plaintext`. Mutually exclusive with `api_key` and + `service_credential`. + """ + + service_credential: VariableOrOptional[ + ModelProviderServiceConfigServiceCredential + ] = None + """ + Reference to a Unity Catalog service credential authorizing Azure OpenAI + requests. On Create, supply `service_credential.name` in the form + `credentials/{name}`. Required on Create when using service-credential + authentication; mutually exclusive with `api_key` and + `entra_service_principal`. The credential is referenced by name; its value + is not carried here. Only supported on Azure-hosted workspaces. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigAzureOpenAiProviderDirectConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict( + self, + ) -> "ModelProviderServiceConfigAzureOpenAiProviderDirectConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigAzureOpenAiProviderDirectConfigDict( + TypedDict, total=False +): + """""" + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + Azure OpenAI API key. Supply the value in `api_key.plaintext`. Mutually + exclusive with Entra ID and Unity Catalog service credential + authentication. + """ + + base_url: VariableOrOptional[str] + """ + Full Azure OpenAI endpoint base URL, e.g. + `https://myresource.openai.azure.com`. Required on Create. + """ + + entra_service_principal: VariableOrOptional[ + ModelProviderServiceConfigEntraServicePrincipalParam + ] + """ + Entra ID service-principal authentication. Set `tenant_id`, `client_id`, + and `client_secret.plaintext`. Mutually exclusive with `api_key` and + `service_credential`. + """ + + service_credential: VariableOrOptional[ + ModelProviderServiceConfigServiceCredentialParam + ] + """ + Reference to a Unity Catalog service credential authorizing Azure OpenAI + requests. On Create, supply `service_credential.name` in the form + `credentials/{name}`. Required on Create when using service-credential + authentication; mutually exclusive with `api_key` and + `entra_service_principal`. The credential is referenced by name; its value + is not carried here. Only supported on Azure-hosted workspaces. + """ + + +ModelProviderServiceConfigAzureOpenAiProviderDirectConfigParam = ( + ModelProviderServiceConfigAzureOpenAiProviderDirectConfigDict + | ModelProviderServiceConfigAzureOpenAiProviderDirectConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_custom_provider_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_custom_provider_config.py new file mode 100644 index 00000000000..e10c0335481 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_custom_provider_config.py @@ -0,0 +1,56 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_custom_provider_direct_config import ( + ModelProviderServiceConfigCustomProviderDirectConfig, + ModelProviderServiceConfigCustomProviderDirectConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigCustomProviderConfig: + """ + Custom OpenAI-compatible provider configuration with bearer-token + authentication. + """ + + direct: VariableOrOptional[ModelProviderServiceConfigCustomProviderDirectConfig] = ( + None + ) + """ + Endpoint and authentication configuration for the custom provider. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigCustomProviderConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigCustomProviderConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigCustomProviderConfigDict(TypedDict, total=False): + """""" + + direct: VariableOrOptional[ + ModelProviderServiceConfigCustomProviderDirectConfigParam + ] + """ + Endpoint and authentication configuration for the custom provider. + """ + + +ModelProviderServiceConfigCustomProviderConfigParam = ( + ModelProviderServiceConfigCustomProviderConfigDict + | ModelProviderServiceConfigCustomProviderConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_custom_provider_direct_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_custom_provider_direct_config.py new file mode 100644 index 00000000000..3a93a834abd --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_custom_provider_direct_config.py @@ -0,0 +1,66 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigCustomProviderDirectConfig: + """ + Direct form of a custom provider configuration. Set `api_key` to the bearer + token sent in the `Authorization` header. + """ + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = None + """ + Bearer token forwarded in the `Authorization` header. Supply the value + in `api_key.plaintext`. + """ + + base_url: VariableOrOptional[str] = None + """ + Endpoint URL of the OpenAI-compatible service (e.g., + `https://api.example.com/v1`). Required on Create. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigCustomProviderDirectConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigCustomProviderDirectConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigCustomProviderDirectConfigDict(TypedDict, total=False): + """""" + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + Bearer token forwarded in the `Authorization` header. Supply the value + in `api_key.plaintext`. + """ + + base_url: VariableOrOptional[str] + """ + Endpoint URL of the OpenAI-compatible service (e.g., + `https://api.example.com/v1`). Required on Create. + """ + + +ModelProviderServiceConfigCustomProviderDirectConfigParam = ( + ModelProviderServiceConfigCustomProviderDirectConfigDict + | ModelProviderServiceConfigCustomProviderDirectConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_entra_service_principal.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_entra_service_principal.py new file mode 100644 index 00000000000..cf5fcefd569 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_entra_service_principal.py @@ -0,0 +1,74 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigEntraServicePrincipal: + """ + Entra ID (Azure AD) service-principal authentication. The `tenant_id` and + `client_id` identify the service principal, and `client_secret` authenticates + it. AI Gateway exchanges these credentials for an Entra bearer token for + requests to Azure OpenAI or Microsoft Foundry. + """ + + client_id: VariableOrOptional[str] = None + """ + Entra ID client (application) ID. Required on Create. + """ + + client_secret: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = None + """ + Entra ID client secret. Supply the value in `client_secret.plaintext`. + """ + + tenant_id: VariableOrOptional[str] = None + """ + Entra ID (Azure AD) tenant ID. Required on Create. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigEntraServicePrincipalDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigEntraServicePrincipalDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigEntraServicePrincipalDict(TypedDict, total=False): + """""" + + client_id: VariableOrOptional[str] + """ + Entra ID client (application) ID. Required on Create. + """ + + client_secret: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + Entra ID client secret. Supply the value in `client_secret.plaintext`. + """ + + tenant_id: VariableOrOptional[str] + """ + Entra ID (Azure AD) tenant ID. Required on Create. + """ + + +ModelProviderServiceConfigEntraServicePrincipalParam = ( + ModelProviderServiceConfigEntraServicePrincipalDict + | ModelProviderServiceConfigEntraServicePrincipal +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_external_model_provider_type.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_external_model_provider_type.py new file mode 100644 index 00000000000..05d65fd793c --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_external_model_provider_type.py @@ -0,0 +1,40 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from enum import Enum +from typing import Literal + + +class ModelProviderServiceConfigExternalModelProviderType(Enum): + """ + External LLM provider for an EXTERNAL_FOUNDATION_MODEL destination. + """ + + EXTERNAL_MODEL_PROVIDER_TYPE_OPENAI = "EXTERNAL_MODEL_PROVIDER_TYPE_OPENAI" + EXTERNAL_MODEL_PROVIDER_TYPE_AZURE_OPENAI = ( + "EXTERNAL_MODEL_PROVIDER_TYPE_AZURE_OPENAI" + ) + EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC = "EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC" + EXTERNAL_MODEL_PROVIDER_TYPE_AMAZON_BEDROCK = ( + "EXTERNAL_MODEL_PROVIDER_TYPE_AMAZON_BEDROCK" + ) + EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM = "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM" + EXTERNAL_MODEL_PROVIDER_TYPE_MICROSOFT_FOUNDRY = ( + "EXTERNAL_MODEL_PROVIDER_TYPE_MICROSOFT_FOUNDRY" + ) + EXTERNAL_MODEL_PROVIDER_TYPE_GEMINI_ENTERPRISE = ( + "EXTERNAL_MODEL_PROVIDER_TYPE_GEMINI_ENTERPRISE" + ) + + +ModelProviderServiceConfigExternalModelProviderTypeParam = ( + Literal[ + "EXTERNAL_MODEL_PROVIDER_TYPE_OPENAI", + "EXTERNAL_MODEL_PROVIDER_TYPE_AZURE_OPENAI", + "EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC", + "EXTERNAL_MODEL_PROVIDER_TYPE_AMAZON_BEDROCK", + "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", + "EXTERNAL_MODEL_PROVIDER_TYPE_MICROSOFT_FOUNDRY", + "EXTERNAL_MODEL_PROVIDER_TYPE_GEMINI_ENTERPRISE", + ] + | ModelProviderServiceConfigExternalModelProviderType +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_gemini_enterprise_provider_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_gemini_enterprise_provider_config.py new file mode 100644 index 00000000000..38f005ad002 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_gemini_enterprise_provider_config.py @@ -0,0 +1,57 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_gemini_enterprise_provider_direct_config import ( + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig, + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigGeminiEnterpriseProviderConfig: + """ + Gemini Enterprise provider configuration. + """ + + direct: VariableOrOptional[ + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig + ] = None + """ + Gemini Enterprise project, region, and authentication configuration. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigGeminiEnterpriseProviderConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigGeminiEnterpriseProviderConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigGeminiEnterpriseProviderConfigDict( + TypedDict, total=False +): + """""" + + direct: VariableOrOptional[ + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigParam + ] + """ + Gemini Enterprise project, region, and authentication configuration. + """ + + +ModelProviderServiceConfigGeminiEnterpriseProviderConfigParam = ( + ModelProviderServiceConfigGeminiEnterpriseProviderConfigDict + | ModelProviderServiceConfigGeminiEnterpriseProviderConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_gemini_enterprise_provider_direct_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_gemini_enterprise_provider_direct_config.py new file mode 100644 index 00000000000..9b35a4cc312 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_gemini_enterprise_provider_direct_config.py @@ -0,0 +1,80 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig: + """ + Direct Gemini Enterprise provider configuration. An API key is required + when creating the service. + """ + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = None + """ + Google Gemini Enterprise API key. Required when creating the service. + Supply the value in `api_key.plaintext`. + """ + + project_id: VariableOrOptional[str] = None + """ + GCP project ID hosting the Gemini Enterprise endpoint. Required on Create. + """ + + region: VariableOrOptional[str] = None + """ + GCP region of the Gemini Enterprise endpoint (e.g., `us-central1`). + Required on Create. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict( + self, + ) -> "ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigDict( + TypedDict, total=False +): + """""" + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + Google Gemini Enterprise API key. Required when creating the service. + Supply the value in `api_key.plaintext`. + """ + + project_id: VariableOrOptional[str] + """ + GCP project ID hosting the Gemini Enterprise endpoint. Required on Create. + """ + + region: VariableOrOptional[str] + """ + GCP region of the Gemini Enterprise endpoint (e.g., `us-central1`). + Required on Create. + """ + + +ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigParam = ( + ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfigDict + | ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_microsoft_foundry_provider_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_microsoft_foundry_provider_config.py new file mode 100644 index 00000000000..c63fe203888 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_microsoft_foundry_provider_config.py @@ -0,0 +1,57 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_microsoft_foundry_provider_direct_config import ( + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig, + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigMicrosoftFoundryProviderConfig: + """ + Microsoft Foundry provider configuration. + """ + + direct: VariableOrOptional[ + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig + ] = None + """ + Microsoft Foundry endpoint and authentication configuration. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigMicrosoftFoundryProviderConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigMicrosoftFoundryProviderConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigMicrosoftFoundryProviderConfigDict( + TypedDict, total=False +): + """""" + + direct: VariableOrOptional[ + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigParam + ] + """ + Microsoft Foundry endpoint and authentication configuration. + """ + + +ModelProviderServiceConfigMicrosoftFoundryProviderConfigParam = ( + ModelProviderServiceConfigMicrosoftFoundryProviderConfigDict + | ModelProviderServiceConfigMicrosoftFoundryProviderConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_microsoft_foundry_provider_direct_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_microsoft_foundry_provider_direct_config.py new file mode 100644 index 00000000000..5caad765601 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_microsoft_foundry_provider_direct_config.py @@ -0,0 +1,134 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_entra_service_principal import ( + ModelProviderServiceConfigEntraServicePrincipal, + ModelProviderServiceConfigEntraServicePrincipalParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config_service_credential import ( + ModelProviderServiceConfigServiceCredential, + ModelProviderServiceConfigServiceCredentialParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig: + """ + Direct form of Microsoft Foundry provider config. + + Authentication is one of three mutually exclusive modes, exactly one of which + must be supplied on Create: + - API key: set `api_key`, leave `entra_service_principal` and + `service_credential` unset. + - Entra ID (service principal): set `entra_service_principal`, leave + `api_key` and `service_credential` unset. AI Gateway exchanges these for + an Entra bearer token on outbound requests via the OAuth2 + client-credentials grant. + - Unity Catalog service credential: set `service_credential.name` to the + resource name `credentials/{name}`, leave `api_key` and + `entra_service_principal` unset. The credential value lives in Unity + Catalog and is referenced by name, not held on this message. Only + supported on Azure-hosted workspaces. + Setting more than one mode is rejected. + """ + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = None + """ + Microsoft Foundry API key. Supply the value in `api_key.plaintext`. + Mutually exclusive with Entra ID and Unity Catalog service credential + authentication. + """ + + base_url: VariableOrOptional[str] = None + """ + Microsoft Foundry endpoint URL. Required on Create. + """ + + entra_service_principal: VariableOrOptional[ + ModelProviderServiceConfigEntraServicePrincipal + ] = None + """ + Entra ID service-principal authentication. Set `tenant_id`, `client_id`, + and `client_secret.plaintext`. Mutually exclusive with `api_key` and + `service_credential`. + """ + + service_credential: VariableOrOptional[ + ModelProviderServiceConfigServiceCredential + ] = None + """ + Reference to a Unity Catalog service credential authorizing Microsoft + Foundry requests. On Create, supply `service_credential.name` in the form + `credentials/{name}`. Required on Create when using service-credential + authentication; mutually exclusive with `api_key` and + `entra_service_principal`. The credential is referenced by name; its value + is not carried here. Only supported on Azure-hosted workspaces. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict( + self, + ) -> "ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigDict( + TypedDict, total=False +): + """""" + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + Microsoft Foundry API key. Supply the value in `api_key.plaintext`. + Mutually exclusive with Entra ID and Unity Catalog service credential + authentication. + """ + + base_url: VariableOrOptional[str] + """ + Microsoft Foundry endpoint URL. Required on Create. + """ + + entra_service_principal: VariableOrOptional[ + ModelProviderServiceConfigEntraServicePrincipalParam + ] + """ + Entra ID service-principal authentication. Set `tenant_id`, `client_id`, + and `client_secret.plaintext`. Mutually exclusive with `api_key` and + `service_credential`. + """ + + service_credential: VariableOrOptional[ + ModelProviderServiceConfigServiceCredentialParam + ] + """ + Reference to a Unity Catalog service credential authorizing Microsoft + Foundry requests. On Create, supply `service_credential.name` in the form + `credentials/{name}`. Required on Create when using service-credential + authentication; mutually exclusive with `api_key` and + `entra_service_principal`. The credential is referenced by name; its value + is not carried here. Only supported on Azure-hosted workspaces. + """ + + +ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigParam = ( + ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfigDict + | ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_model_target_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_model_target_config.py new file mode 100644 index 00000000000..9625c038b3e --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_model_target_config.py @@ -0,0 +1,67 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr, VariableOrList + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigModelTargetConfig: + """ + Model target configuration for an external model destination. + """ + + model: VariableOr[str] + """ + Provider-side model identifier, such as `gpt-5` or `claude-opus-4-7`. + This identifies a model at the upstream provider; it is not a Unity + Catalog model resource. + """ + + native_api_types: VariableOrList[str] = field(default_factory=list) + """ + Provider-native API types supported by this model, such as + `openai/v1/chat/completions`. At least one value is required. AI Gateway + uses these values to translate requests and responses. At most 64 entries + of 256 characters each are allowed. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigModelTargetConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigModelTargetConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigModelTargetConfigDict(TypedDict, total=False): + """""" + + model: VariableOr[str] + """ + Provider-side model identifier, such as `gpt-5` or `claude-opus-4-7`. + This identifies a model at the upstream provider; it is not a Unity + Catalog model resource. + """ + + native_api_types: VariableOrList[str] + """ + Provider-native API types supported by this model, such as + `openai/v1/chat/completions`. At least one value is required. AI Gateway + uses these values to translate requests and responses. At most 64 entries + of 256 characters each are allowed. + """ + + +ModelProviderServiceConfigModelTargetConfigParam = ( + ModelProviderServiceConfigModelTargetConfigDict + | ModelProviderServiceConfigModelTargetConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_open_ai_provider_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_open_ai_provider_config.py new file mode 100644 index 00000000000..70200550381 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_open_ai_provider_config.py @@ -0,0 +1,55 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_open_ai_provider_direct_config import ( + ModelProviderServiceConfigOpenAiProviderDirectConfig, + ModelProviderServiceConfigOpenAiProviderDirectConfigParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigOpenAiProviderConfig: + """ + OpenAI provider configuration. + """ + + direct: VariableOrOptional[ModelProviderServiceConfigOpenAiProviderDirectConfig] = ( + None + ) + """ + OpenAI configuration with an API key supplied in the request. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigOpenAiProviderConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigOpenAiProviderConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigOpenAiProviderConfigDict(TypedDict, total=False): + """""" + + direct: VariableOrOptional[ + ModelProviderServiceConfigOpenAiProviderDirectConfigParam + ] + """ + OpenAI configuration with an API key supplied in the request. + """ + + +ModelProviderServiceConfigOpenAiProviderConfigParam = ( + ModelProviderServiceConfigOpenAiProviderConfigDict + | ModelProviderServiceConfigOpenAiProviderConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_open_ai_provider_direct_config.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_open_ai_provider_direct_config.py new file mode 100644 index 00000000000..128ce50e14e --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_open_ai_provider_direct_config.py @@ -0,0 +1,77 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional +from databricks.bundles.model_provider_services._models.model_provider_service_config_provider_secret import ( + ModelProviderServiceConfigProviderSecret, + ModelProviderServiceConfigProviderSecretParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigOpenAiProviderDirectConfig: + """ + Direct (inline-credentials) form of the OpenAI provider config. + """ + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecret] = None + """ + OpenAI API key. Required when creating the service. Supply the value in + `api_key.plaintext`. + """ + + base_url: VariableOrOptional[str] = None + """ + Optional custom base URL. Defaults to `https://api.openai.com/v1`. Use for + OpenAI-API-compatible third-party endpoints or in-network proxies. + """ + + organization: VariableOrOptional[str] = None + """ + Optional OpenAI organization ID. When set, the platform forwards it as + the `OpenAI-Organization` header. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigOpenAiProviderDirectConfigDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigOpenAiProviderDirectConfigDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigOpenAiProviderDirectConfigDict(TypedDict, total=False): + """""" + + api_key: VariableOrOptional[ModelProviderServiceConfigProviderSecretParam] + """ + OpenAI API key. Required when creating the service. Supply the value in + `api_key.plaintext`. + """ + + base_url: VariableOrOptional[str] + """ + Optional custom base URL. Defaults to `https://api.openai.com/v1`. Use for + OpenAI-API-compatible third-party endpoints or in-network proxies. + """ + + organization: VariableOrOptional[str] + """ + Optional OpenAI organization ID. When set, the platform forwards it as + the `OpenAI-Organization` header. + """ + + +ModelProviderServiceConfigOpenAiProviderDirectConfigParam = ( + ModelProviderServiceConfigOpenAiProviderDirectConfigDict + | ModelProviderServiceConfigOpenAiProviderDirectConfig +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_provider_secret.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_provider_secret.py new file mode 100644 index 00000000000..9b36c6ec54f --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_provider_secret.py @@ -0,0 +1,52 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOrOptional + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigProviderSecret: + """ + A secret value supplied as part of an inline provider config. The caller + supplies the value as inline `plaintext` on writes; the platform stores it + encrypted. The `plaintext` field is `INPUT_ONLY` and never round-trips on + reads. + """ + + plaintext: VariableOrOptional[str] = None + """ + Inline plaintext credential. INPUT_ONLY: the value never round-trips on + reads. Get and List responses omit `plaintext`; the enclosing secret + object remains present to indicate that a secret is configured. + """ + + @classmethod + def from_dict(cls, value: "ModelProviderServiceConfigProviderSecretDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigProviderSecretDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigProviderSecretDict(TypedDict, total=False): + """""" + + plaintext: VariableOrOptional[str] + """ + Inline plaintext credential. INPUT_ONLY: the value never round-trips on + reads. Get and List responses omit `plaintext`; the enclosing secret + object remains present to indicate that a secret is configured. + """ + + +ModelProviderServiceConfigProviderSecretParam = ( + ModelProviderServiceConfigProviderSecretDict + | ModelProviderServiceConfigProviderSecret +) diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_service_credential.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_service_credential.py new file mode 100644 index 00000000000..12753ea2865 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service_config_service_credential.py @@ -0,0 +1,54 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class ModelProviderServiceConfigServiceCredential: + """ + The customer-owned Unity Catalog service credential a model provider service + uses to authenticate to its provider, referenced by name. + """ + + name: VariableOr[str] + """ + Resource name of the bound Unity Catalog service credential, in the form + `credentials/{name}`. Supply this field when creating the service or + rebinding its credential. On read, it reflects the credential's current + name. + """ + + @classmethod + def from_dict( + cls, value: "ModelProviderServiceConfigServiceCredentialDict" + ) -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "ModelProviderServiceConfigServiceCredentialDict": + return _transform_to_json_value(self) # type:ignore + + +class ModelProviderServiceConfigServiceCredentialDict(TypedDict, total=False): + """""" + + name: VariableOr[str] + """ + Resource name of the bound Unity Catalog service credential, in the form + `credentials/{name}`. Supply this field when creating the service or + rebinding its credential. On read, it reflects the credential's current + name. + """ + + +ModelProviderServiceConfigServiceCredentialParam = ( + ModelProviderServiceConfigServiceCredentialDict + | ModelProviderServiceConfigServiceCredential +) diff --git a/python/databricks/bundles/model_provider_services/_models/rate_limit.py b/python/databricks/bundles/model_provider_services/_models/rate_limit.py new file mode 100644 index 00000000000..5bbb533157a --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/rate_limit.py @@ -0,0 +1,102 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from dataclasses import dataclass +from typing import TYPE_CHECKING, TypedDict + +from databricks.bundles.core._transform import _transform +from databricks.bundles.core._transform_to_json import _transform_to_json_value +from databricks.bundles.core._variable import VariableOr, VariableOrOptional +from databricks.bundles.model_provider_services._models.rate_limit_rate_limit_key import ( + RateLimitRateLimitKey, + RateLimitRateLimitKeyParam, +) +from databricks.bundles.model_provider_services._models.rate_limit_rate_limit_renewal_period import ( + RateLimitRateLimitRenewalPeriod, + RateLimitRateLimitRenewalPeriodParam, +) + +if TYPE_CHECKING: + from typing_extensions import Self + + +@dataclass(kw_only=True) +class RateLimit: + """ + A rate limit applied to service requests. Leave `requests` or `tokens` + unset to impose no limit on that dimension; set a value to cap that dimension + within the renewal period. + """ + + key: VariableOr[RateLimitRateLimitKey] + """ + Scope of the rate limit. Depending on this value, the limit applies to a + principal, the service as a whole, or each user by default. + """ + + renewal_period: VariableOr[RateLimitRateLimitRenewalPeriod] + """ + Renewal period. + """ + + principal: VariableOrOptional[str] = None + """ + Principal this limit applies to: user email, group name, or service + principal application ID. Required when `key` applies to a user, group, or + service principal; otherwise it must be unset. + """ + + requests: VariableOrOptional[int] = None + """ + Maximum requests allowed in one renewal period. Leave unset for no request + limit. Set to `0` to deny all requests. + """ + + tokens: VariableOrOptional[int] = None + """ + Maximum tokens allowed in one renewal period. Leave unset for no token + limit. Set to `0` to deny all requests. + """ + + @classmethod + def from_dict(cls, value: "RateLimitDict") -> "Self": + return _transform(cls, value) + + def as_dict(self) -> "RateLimitDict": + return _transform_to_json_value(self) # type:ignore + + +class RateLimitDict(TypedDict, total=False): + """""" + + key: VariableOr[RateLimitRateLimitKeyParam] + """ + Scope of the rate limit. Depending on this value, the limit applies to a + principal, the service as a whole, or each user by default. + """ + + renewal_period: VariableOr[RateLimitRateLimitRenewalPeriodParam] + """ + Renewal period. + """ + + principal: VariableOrOptional[str] + """ + Principal this limit applies to: user email, group name, or service + principal application ID. Required when `key` applies to a user, group, or + service principal; otherwise it must be unset. + """ + + requests: VariableOrOptional[int] + """ + Maximum requests allowed in one renewal period. Leave unset for no request + limit. Set to `0` to deny all requests. + """ + + tokens: VariableOrOptional[int] + """ + Maximum tokens allowed in one renewal period. Leave unset for no token + limit. Set to `0` to deny all requests. + """ + + +RateLimitParam = RateLimitDict | RateLimit diff --git a/python/databricks/bundles/model_provider_services/_models/rate_limit_rate_limit_key.py b/python/databricks/bundles/model_provider_services/_models/rate_limit_rate_limit_key.py new file mode 100644 index 00000000000..fb907da810f --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/rate_limit_rate_limit_key.py @@ -0,0 +1,28 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from enum import Enum +from typing import Literal + + +class RateLimitRateLimitKey(Enum): + """ + Scope key for a rate limit. + """ + + RATE_LIMIT_KEY_USER = "RATE_LIMIT_KEY_USER" + RATE_LIMIT_KEY_USER_GROUP = "RATE_LIMIT_KEY_USER_GROUP" + RATE_LIMIT_KEY_SERVICE_PRINCIPAL = "RATE_LIMIT_KEY_SERVICE_PRINCIPAL" + RATE_LIMIT_KEY_SERVICE = "RATE_LIMIT_KEY_SERVICE" + RATE_LIMIT_KEY_USER_DEFAULT = "RATE_LIMIT_KEY_USER_DEFAULT" + + +RateLimitRateLimitKeyParam = ( + Literal[ + "RATE_LIMIT_KEY_USER", + "RATE_LIMIT_KEY_USER_GROUP", + "RATE_LIMIT_KEY_SERVICE_PRINCIPAL", + "RATE_LIMIT_KEY_SERVICE", + "RATE_LIMIT_KEY_USER_DEFAULT", + ] + | RateLimitRateLimitKey +) diff --git a/python/databricks/bundles/model_provider_services/_models/rate_limit_rate_limit_renewal_period.py b/python/databricks/bundles/model_provider_services/_models/rate_limit_rate_limit_renewal_period.py new file mode 100644 index 00000000000..58636c784c0 --- /dev/null +++ b/python/databricks/bundles/model_provider_services/_models/rate_limit_rate_limit_renewal_period.py @@ -0,0 +1,19 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from enum import Enum +from typing import Literal + + +class RateLimitRateLimitRenewalPeriod(Enum): + """ + Renewal period for a rate limit. + """ + + RATE_LIMIT_RENEWAL_PERIOD_MINUTE = "RATE_LIMIT_RENEWAL_PERIOD_MINUTE" + RATE_LIMIT_RENEWAL_PERIOD_HOUR = "RATE_LIMIT_RENEWAL_PERIOD_HOUR" + + +RateLimitRateLimitRenewalPeriodParam = ( + Literal["RATE_LIMIT_RENEWAL_PERIOD_MINUTE", "RATE_LIMIT_RENEWAL_PERIOD_HOUR"] + | RateLimitRateLimitRenewalPeriod +) diff --git a/python/databricks_tests/core/_generated/__init__.py b/python/databricks_tests/core/_generated/__init__.py index 4ab30185d16..486f70d1a64 100644 --- a/python/databricks_tests/core/_generated/__init__.py +++ b/python/databricks_tests/core/_generated/__init__.py @@ -13,6 +13,7 @@ job_runs, jobs, mcp_services, + model_provider_services, model_services, model_serving_endpoints, models, @@ -45,6 +46,7 @@ job_runs._test_case(), jobs._test_case(), mcp_services._test_case(), + model_provider_services._test_case(), model_services._test_case(), model_serving_endpoints._test_case(), models._test_case(), diff --git a/python/databricks_tests/core/_generated/model_provider_services.py b/python/databricks_tests/core/_generated/model_provider_services.py new file mode 100644 index 00000000000..f91592daebb --- /dev/null +++ b/python/databricks_tests/core/_generated/model_provider_services.py @@ -0,0 +1,34 @@ +# Code generated by pydabs-codegen. DO NOT EDIT. + +from databricks.bundles.core import Resources, model_provider_service_mutator +from databricks.bundles.core._generated.model_provider_services import _resource_type +from databricks.bundles.model_provider_services._models.lifecycle import Lifecycle +from databricks.bundles.model_provider_services._models.model_provider_service import ( + ModelProviderService, +) +from databricks.bundles.model_provider_services._models.model_provider_service_config import ( + ModelProviderServiceConfig, +) +from databricks_tests.core._resource_test_case import ResourceTestCase + + +def _test_case(): + return ( + ResourceTestCase( + add_resource=Resources.add_model_provider_service, + dict_example={ + "config": {}, + "lifecycle": {}, + "model_provider_service_id": "model_provider_service_id", + "parent": "parent", + }, + dataclass_example=ModelProviderService( + config=ModelProviderServiceConfig(), + lifecycle=Lifecycle(), + model_provider_service_id="model_provider_service_id", + parent="parent", + ), + mutator=model_provider_service_mutator, + ), + _resource_type(), + ) diff --git a/python/databricks_tests/core/public_api.txt b/python/databricks_tests/core/public_api.txt index 5e6a64a1dcd..0c259ce5dfb 100644 --- a/python/databricks_tests/core/public_api.txt +++ b/python/databricks_tests/core/public_api.txt @@ -30,6 +30,7 @@ __all__ = [ mcp_service_mutator, mlflow_experiment_mutator, mlflow_model_mutator, + model_provider_service_mutator, model_service_mutator, model_serving_endpoint_mutator, pipeline_mutator, @@ -103,6 +104,7 @@ class Resources: def add_mcp_service(self, resource_name: str, mcp_service: McpServiceParam, *, location: Union[Location, None] = None) -> None def add_mlflow_experiment(self, resource_name: str, mlflow_experiment: MlflowExperimentParam, *, location: Union[Location, None] = None) -> None def add_mlflow_model(self, resource_name: str, mlflow_model: MlflowModelParam, *, location: Union[Location, None] = None) -> None + def add_model_provider_service(self, resource_name: str, model_provider_service: ModelProviderServiceParam, *, location: Union[Location, None] = None) -> None def add_model_service(self, resource_name: str, model_service: ModelServiceParam, *, location: Union[Location, None] = None) -> None def add_model_serving_endpoint(self, resource_name: str, model_serving_endpoint: ModelServingEndpointParam, *, location: Union[Location, None] = None) -> None def add_pipeline(self, resource_name: str, pipeline: PipelineParam, *, location: Union[Location, None] = None) -> None @@ -132,6 +134,7 @@ class Resources: @property job_runs -> dict[str, JobRun] @property jobs -> dict[str, Job] @property mcp_services -> dict[str, McpService] + @property model_provider_services -> dict[str, ModelProviderService] @property model_services -> dict[str, ModelService] @property model_serving_endpoints -> dict[str, ModelServingEndpoint] @property models -> dict[str, MlflowModel] @@ -225,6 +228,10 @@ def mlflow_experiment_mutator(function: Callable) -> ResourceMutator[MlflowExper @overload def mlflow_model_mutator(function: Callable[[MlflowModel], MlflowModel]) -> ResourceMutator[MlflowModel] def mlflow_model_mutator(function: Callable) -> ResourceMutator[MlflowModel] +@overload def model_provider_service_mutator(function: Callable[[Bundle, ModelProviderService], ModelProviderService]) -> ResourceMutator[ModelProviderService] +@overload def model_provider_service_mutator(function: Callable[[ModelProviderService], ModelProviderService]) -> ResourceMutator[ModelProviderService] +def model_provider_service_mutator(function: Callable) -> ResourceMutator[ModelProviderService] + @overload def model_service_mutator(function: Callable[[Bundle, ModelService], ModelService]) -> ResourceMutator[ModelService] @overload def model_service_mutator(function: Callable[[ModelService], ModelService]) -> ResourceMutator[ModelService] def model_service_mutator(function: Callable) -> ResourceMutator[ModelService] @@ -297,6 +304,7 @@ singular_name=job_run plural_name=job_runs resource_type=JobRun singular_name=mcp_service plural_name=mcp_services resource_type=McpService singular_name=mlflow_experiment plural_name=experiments resource_type=MlflowExperiment singular_name=mlflow_model plural_name=models resource_type=MlflowModel +singular_name=model_provider_service plural_name=model_provider_services resource_type=ModelProviderService singular_name=model_service plural_name=model_services resource_type=ModelService singular_name=model_serving_endpoint plural_name=model_serving_endpoints resource_type=ModelServingEndpoint singular_name=pipeline plural_name=pipelines resource_type=Pipeline