From 7dd783213b8e8e3c1fbf72121fe1acbf1d8747e4 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 9 Oct 2026 10:41:52 +0200 Subject: [PATCH 1/2] feat: support model_service resources in apps init Unity AI Gateway model services are an app resource bound as a uc_securable with securable_type MODEL_SERVICE and EXECUTE permission. Add the model_service type to apps init: a catalog, schema, and model service picker backed by AiGateway.ListModelServices, and a built-in binding spec for manifests that do not declare one. The picked value is the model service full name (catalog.schema.name), without the model-services/ resource prefix. Co-authored-by: Isaac Signed-off-by: MarioCadenas --- .nextchanges/cli/apps-init-model-service.md | 1 + libs/apps/generator/generator.go | 6 ++++ libs/apps/generator/generator_test.go | 12 ++++++++ libs/apps/prompt/listers.go | 27 +++++++++++++++++ libs/apps/prompt/listers_test.go | 32 +++++++++++++++++++++ libs/apps/prompt/prefetch.go | 9 +++--- libs/apps/prompt/prompt.go | 5 ++++ libs/apps/prompt/resource_registry.go | 3 ++ 8 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 .nextchanges/cli/apps-init-model-service.md create mode 100644 libs/apps/prompt/listers_test.go diff --git a/.nextchanges/cli/apps-init-model-service.md b/.nextchanges/cli/apps-init-model-service.md new file mode 100644 index 00000000000..ae83d1218f1 --- /dev/null +++ b/.nextchanges/cli/apps-init-model-service.md @@ -0,0 +1 @@ +* `databricks apps init` now supports `model_service` (Unity AI Gateway model service) resources, with a catalog, schema, and model service picker, bound as a `uc_securable` with `securable_type: MODEL_SERVICE` and `EXECUTE` permission. diff --git a/libs/apps/generator/generator.go b/libs/apps/generator/generator.go index 3f99d80f900..6f5d668cc71 100644 --- a/libs/apps/generator/generator.go +++ b/libs/apps/generator/generator.go @@ -422,6 +422,12 @@ var appResourceSpecs = map[string]appResourceSpec{ staticFields: [][2]string{{"securable_type", "TABLE"}}, permission: "SELECT", }, + "model_service": { + yamlKey: "uc_securable", + varFields: [][2]string{{"name", "securable_full_name"}}, + staticFields: [][2]string{{"securable_type", "MODEL_SERVICE"}}, + permission: "EXECUTE", + }, // TODO: uncomment when bundles support app as an app resource type. // "app": { // yamlKey: "app", diff --git a/libs/apps/generator/generator_test.go b/libs/apps/generator/generator_test.go index d7a1de85cc8..8b874855441 100644 --- a/libs/apps/generator/generator_test.go +++ b/libs/apps/generator/generator_test.go @@ -249,6 +249,7 @@ func TestGenerateBundleResourcesDefaultPermissions(t *testing.T) { {"uc_connection", "USE_CONNECTION"}, {"genie_space", "CAN_VIEW"}, {"vector_search_index", "SELECT"}, + {"model_service", "EXECUTE"}, // TODO: uncomment when bundles support app as an app resource type. // {"app", "CAN_USE"}, } @@ -466,6 +467,17 @@ func TestGenerateResourceYAMLAllTypes(t *testing.T) { "permission: EXECUTE", }, }, + { + name: "model_service maps to uc_securable MODEL_SERVICE", + resource: manifest.Resource{Type: "model_service", Alias: "Model Service", ResourceKey: "model", Permission: "EXECUTE"}, + expectContains: []string{ + "- name: model", + "uc_securable:", + "securable_full_name: ${var.model_name}", + "securable_type: MODEL_SERVICE", + "permission: EXECUTE", + }, + }, { name: "uc_connection maps to uc_securable CONNECTION", resource: manifest.Resource{Type: "uc_connection", Alias: "UC Connection", ResourceKey: "conn", Permission: "USE_CONNECTION"}, diff --git a/libs/apps/prompt/listers.go b/libs/apps/prompt/listers.go index 4bc2b1e301e..1745b103255 100644 --- a/libs/apps/prompt/listers.go +++ b/libs/apps/prompt/listers.go @@ -130,6 +130,33 @@ func ListVolumesInSchema(ctx context.Context, catalogName, schemaName string) ([ return capResults(out), nil } +// modelServiceNamePrefix prefixes model service resource names +// (model-services/{catalog}.{schema}.{name}); apps reference the bare full name. +const modelServiceNamePrefix = "model-services/" + +// ListModelServicesInSchema returns model services within a catalog.schema as selectable items. +// Item IDs are the full names (catalog.schema.name) used as the app's uc_securable. +func ListModelServicesInSchema(ctx context.Context, catalogName, schemaName string) ([]ListItem, error) { + w, err := workspaceClient(ctx) + if err != nil { + return nil, err + } + iter := w.AiGateway.ListModelServices(ctx, catalog.ListModelServicesRequest{ + Parent: "schemas/" + catalogName + "." + schemaName, + }) + services, err := listing.ToSlice(ctx, iter) + if err != nil { + return nil, err + } + out := make([]ListItem, 0, min(len(services), maxListResults)) + for _, s := range services { + fullName := strings.TrimPrefix(s.Name, modelServiceNamePrefix) + label := fullName[strings.LastIndex(fullName, ".")+1:] + out = append(out, ListItem{ID: fullName, Label: label}) + } + return capResults(out), nil +} + // ListFunctionsInSchema returns UC functions within a catalog.schema as selectable items. func ListFunctionsInSchema(ctx context.Context, catalogName, schemaName string) ([]ListItem, error) { w, err := workspaceClient(ctx) diff --git a/libs/apps/prompt/listers_test.go b/libs/apps/prompt/listers_test.go new file mode 100644 index 00000000000..79e68ae8e08 --- /dev/null +++ b/libs/apps/prompt/listers_test.go @@ -0,0 +1,32 @@ +package prompt + +import ( + "testing" + + "github.com/databricks/cli/libs/cmdctx" + "github.com/databricks/cli/libs/cmdio" + "github.com/databricks/databricks-sdk-go/experimental/mocks" + "github.com/databricks/databricks-sdk-go/listing" + "github.com/databricks/databricks-sdk-go/service/catalog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestListModelServicesInSchema(t *testing.T) { + m := mocks.NewMockWorkspaceClient(t) + ctx := cmdctx.SetWorkspaceClient(cmdio.MockDiscard(t.Context()), m.WorkspaceClient) + + m.GetMockAiGatewayAPI().EXPECT(). + ListModelServices(ctx, catalog.ListModelServicesRequest{Parent: "schemas/main.default"}). + Return(&listing.SliceIterator[catalog.ModelService]{ + {Name: "model-services/main.default.claude"}, + {Name: "model-services/main.default.gpt"}, + }) + + items, err := ListModelServicesInSchema(ctx, "main", "default") + require.NoError(t, err) + assert.Equal(t, []ListItem{ + {ID: "main.default.claude", Label: "claude"}, + {ID: "main.default.gpt", Label: "gpt"}, + }, items) +} diff --git a/libs/apps/prompt/prefetch.go b/libs/apps/prompt/prefetch.go index d3f38bade08..59373ba107a 100644 --- a/libs/apps/prompt/prefetch.go +++ b/libs/apps/prompt/prefetch.go @@ -34,10 +34,11 @@ var firstStepPrefetch = map[string]struct { cacheKey string ctor pagedConstructor }{ - ResourceTypeVolume: {cacheKeyCatalogs, ListCatalogs}, - ResourceTypeUCFunction: {cacheKeyCatalogs, ListCatalogs}, - ResourceTypeDatabase: {cacheKeyDatabaseInstances, ListDatabaseInstances}, - ResourceTypePostgres: {cacheKeyPostgresProjects, ListPostgresProjects}, + ResourceTypeVolume: {cacheKeyCatalogs, ListCatalogs}, + ResourceTypeUCFunction: {cacheKeyCatalogs, ListCatalogs}, + ResourceTypeModelService: {cacheKeyCatalogs, ListCatalogs}, + ResourceTypeDatabase: {cacheKeyDatabaseInstances, ListDatabaseInstances}, + ResourceTypePostgres: {cacheKeyPostgresProjects, ListPostgresProjects}, } // PrefetchResources kicks off a background goroutine for every resource type diff --git a/libs/apps/prompt/prompt.go b/libs/apps/prompt/prompt.go index 6bbbaaa2d28..0c934904f7b 100644 --- a/libs/apps/prompt/prompt.go +++ b/libs/apps/prompt/prompt.go @@ -852,6 +852,11 @@ func PromptForUCFunction(ctx context.Context, r manifest.Resource, required bool return promptUCResource(ctx, r, required, "UC Function", "Fetching functions...", ListFunctionsInSchema) } +// PromptForModelService shows a three-step picker for model services: catalog -> schema -> model service. +func PromptForModelService(ctx context.Context, r manifest.Resource, required bool) (map[string]string, error) { + return promptUCResource(ctx, r, required, "Model Service", "Fetching model services...", ListModelServicesInSchema) +} + // PromptForUCConnection shows a picker for UC connections. func PromptForUCConnection(ctx context.Context, r manifest.Resource, required bool) (map[string]string, error) { title := resourceTitle("Select UC Connection", r) diff --git a/libs/apps/prompt/resource_registry.go b/libs/apps/prompt/resource_registry.go index e6846ad2f2b..72d6163c85b 100644 --- a/libs/apps/prompt/resource_registry.go +++ b/libs/apps/prompt/resource_registry.go @@ -20,6 +20,7 @@ const ( ResourceTypePostgres = "postgres" // Lakebase Autoscaling (V2) ResourceTypeGenieSpace = "genie_space" ResourceTypeExperiment = "experiment" + ResourceTypeModelService = "model_service" // Unity AI Gateway model service // TODO: uncomment when bundles support app as an app resource type. // ResourceTypeApp = "app" ) @@ -72,6 +73,8 @@ func GetPromptFunc(resourceType string) (PromptResourceFunc, bool) { return PromptForGenieSpace, true case ResourceTypeExperiment: return PromptForExperiment, true + case ResourceTypeModelService: + return PromptForModelService, true // TODO: uncomment when bundles support app as an app resource type. // case ResourceTypeApp: // return PromptForAppResource, true From aa22e79e5e4241ffc00a4c211af2047b56250436 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 9 Oct 2026 10:42:43 +0200 Subject: [PATCH 2/2] docs: link PR in changelog fragment Co-authored-by: Isaac Signed-off-by: MarioCadenas --- .nextchanges/cli/apps-init-model-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nextchanges/cli/apps-init-model-service.md b/.nextchanges/cli/apps-init-model-service.md index ae83d1218f1..26ec784bb4b 100644 --- a/.nextchanges/cli/apps-init-model-service.md +++ b/.nextchanges/cli/apps-init-model-service.md @@ -1 +1 @@ -* `databricks apps init` now supports `model_service` (Unity AI Gateway model service) resources, with a catalog, schema, and model service picker, bound as a `uc_securable` with `securable_type: MODEL_SERVICE` and `EXECUTE` permission. +* `databricks apps init` now supports `model_service` (Unity AI Gateway model service) resources, with a catalog, schema, and model service picker, bound as a `uc_securable` with `securable_type: MODEL_SERVICE` and `EXECUTE` permission. ([#6998](https://github.com/databricks/cli/pull/6998))