From b255a8f688e0445d2b6dcc0622699f614d7216c2 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 9 Oct 2026 17:32:35 +0200 Subject: [PATCH] feat(shared): add a modular modelServing client to the workspace client Adds a lazily built `modelServing` accessor backed by @databricks/sdk-modelserving 0.53.0 (via mapToClientOptions), so the CLI can drop raw request() calls for serving endpoint get/list. The legacy `servingEndpoints` accessor is unchanged. Co-authored-by: Isaac Signed-off-by: MarioCadenas --- knip.json | 1 + packages/appkit/package.json | 1 + .../src/testing/mock-workspace-client.ts | 1 + packages/shared/package.json | 1 + .../shared/src/workspace-client/client.ts | 12 +++++++ .../shared/src/workspace-client/modular.ts | 22 +++++++++++++ .../workspace-client/tests/modular.test.ts | 32 +++++++++++++++++++ packages/shared/src/workspace-client/types.ts | 4 +++ pnpm-lock.yaml | 19 +++++++++++ 9 files changed, 93 insertions(+) diff --git a/knip.json b/knip.json index 7af9ba72f..6a98bd010 100644 --- a/knip.json +++ b/knip.json @@ -17,6 +17,7 @@ "@databricks/sdk-genie", "@databricks/sdk-jobs", "@databricks/sdk-postgres", + "@databricks/sdk-modelserving", "@databricks/sdk-uc-tables", "@databricks/sdk-vectorsearch", "@databricks/sdk-options", diff --git a/packages/appkit/package.json b/packages/appkit/package.json index 445adfaa8..a5d132e32 100644 --- a/packages/appkit/package.json +++ b/packages/appkit/package.json @@ -77,6 +77,7 @@ "@databricks/sdk-files": "0.52.0", "@databricks/sdk-genie": "0.54.0", "@databricks/sdk-jobs": "0.57.0", + "@databricks/sdk-modelserving": "0.53.0", "@databricks/sdk-uc-tables": "0.52.0", "@databricks/sdk-vectorsearch": "0.51.0", "@databricks/sdk-options": "0.51.0", diff --git a/packages/appkit/src/testing/mock-workspace-client.ts b/packages/appkit/src/testing/mock-workspace-client.ts index 24da241fe..adcd8b777 100644 --- a/packages/appkit/src/testing/mock-workspace-client.ts +++ b/packages/appkit/src/testing/mock-workspace-client.ts @@ -73,6 +73,7 @@ const FACADE_SERVICES = [ "tables", "postgres", "statementExecution", + "modelServing", "servingEndpoints", "currentUser", ] as const; diff --git a/packages/shared/package.json b/packages/shared/package.json index de317aecf..485dfe132 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -54,6 +54,7 @@ "@databricks/sdk-files": "0.52.0", "@databricks/sdk-genie": "0.54.0", "@databricks/sdk-jobs": "0.57.0", + "@databricks/sdk-modelserving": "0.53.0", "@databricks/sdk-uc-tables": "0.52.0", "@databricks/sdk-vectorsearch": "0.51.0", "@databricks/sdk-options": "0.51.0", diff --git a/packages/shared/src/workspace-client/client.ts b/packages/shared/src/workspace-client/client.ts index c704f95a4..29d272d66 100644 --- a/packages/shared/src/workspace-client/client.ts +++ b/packages/shared/src/workspace-client/client.ts @@ -22,6 +22,8 @@ import { type GenieClient, buildJobsClient, type JobsClient, + buildModelServingClient, + type ModelServingClient, buildFilesClient, type FilesClient, buildTablesClient, @@ -46,6 +48,7 @@ export class AppKitWorkspaceClient implements WorkspaceClient { #currentUser?: ScimClient; #genie?: GenieClient; #jobs?: JobsClient; + #modelServing?: ModelServingClient; #files?: FilesClient; #vectorSearch?: VectorSearchClient; #tables?: TablesClient; @@ -119,6 +122,15 @@ export class AppKitWorkspaceClient implements WorkspaceClient { return this.#statementExecution; } + // Modular serving endpoints client. A new accessor because `servingEndpoints` + // is the legacy client's public type and can't change. + get modelServing(): ModelServingClient { + if (!this.#modelServing) { + this.#modelServing = buildModelServingClient(this.#opts); + } + return this.#modelServing; + } + get servingEndpoints() { return this.#getLegacy().servingEndpoints; } diff --git a/packages/shared/src/workspace-client/modular.ts b/packages/shared/src/workspace-client/modular.ts index 3fe97d6be..d077cf542 100644 --- a/packages/shared/src/workspace-client/modular.ts +++ b/packages/shared/src/workspace-client/modular.ts @@ -55,6 +55,7 @@ import { type ProfileOptions, resolve } from "@databricks/sdk-core/profiles"; import type { FilesClient } from "@databricks/sdk-files/v2"; import type { GenieClient } from "@databricks/sdk-genie/v1"; import type { JobsClient } from "@databricks/sdk-jobs/v2"; +import type { ModelServingClient } from "@databricks/sdk-modelserving/v1"; import type { ClientOptions } from "@databricks/sdk-options/client"; import type { PostgresClient } from "@databricks/sdk-postgres/v1"; import { ScimClient } from "@databricks/sdk-scim/v1"; @@ -616,6 +617,19 @@ export function buildVectorSearchClient( ); } +/** Build a modular Model Serving (serving endpoints) client from wrapper options. */ +export function buildModelServingClient( + opts: WorkspaceClientOptions, +): ModelServingClient { + return lazyClient( + () => + import("@databricks/sdk-modelserving/v1").then( + (m) => m.ModelServingClient, + ), + mapToClientOptions(opts), + ); +} + /** Build a modular Unity Catalog Tables client from wrapper options. */ export function buildTablesClient(opts: WorkspaceClientOptions): TablesClient { return lazyClient( @@ -646,6 +660,7 @@ export type { } from "@databricks/sdk-files/v2"; export type { GenieClient } from "@databricks/sdk-genie/v1"; export type { JobsClient } from "@databricks/sdk-jobs/v2"; +export type { ModelServingClient } from "@databricks/sdk-modelserving/v1"; export type { PostgresClient } from "@databricks/sdk-postgres/v1"; export type { ScimClient } from "@databricks/sdk-scim/v1"; export type { StatementExecutionClient } from "@databricks/sdk-statementexecution/v1"; @@ -692,6 +707,13 @@ export type { SubmitRunRequest, SubmitRunResponse, } from "@databricks/sdk-jobs/v2"; +export type { + GetInferenceEndpointRequest, + InferenceEndpoint, + InferenceEndpointDetailed, + ListInferenceEndpointsRequest, + ListInferenceEndpointsResponse, +} from "@databricks/sdk-modelserving/v1"; export type { EndpointHealth, EndpointInfo, diff --git a/packages/shared/src/workspace-client/tests/modular.test.ts b/packages/shared/src/workspace-client/tests/modular.test.ts index 383dacf36..f7c4ff195 100644 --- a/packages/shared/src/workspace-client/tests/modular.test.ts +++ b/packages/shared/src/workspace-client/tests/modular.test.ts @@ -55,6 +55,12 @@ vi.mock("@databricks/sdk-genie/v1", () => ({ }; }), })); +vi.mock("@databricks/sdk-modelserving/v1", () => ({ + ModelServingClient: vi.fn().mockImplementation((opts) => { + ctorOpts.push(opts); + return { opts, getInferenceEndpoint: async () => ({}) }; + }), +})); vi.mock("@databricks/sdk-postgres/v1", () => ({ PostgresClient: vi.fn().mockImplementation((opts) => { ctorOpts.push(opts); @@ -125,6 +131,7 @@ vi.mock("node:child_process", () => ({ import { ApiError } from "../errors"; import { buildGenieClient, + buildModelServingClient, buildWarehousesClient, buildPostgresClient, buildWorkspaceAuth, @@ -352,6 +359,31 @@ describe("modular mapToClientOptions (via buildWarehousesClient)", () => { expect(Reflect.get(client, "then")).toBeUndefined(); }); + test("modelServing normalizes the host and passes SP M2M credentials", async () => { + process.env.DATABRICKS_HOST = "envhost.cloud.databricks.com"; + process.env.DATABRICKS_CLIENT_ID = "sp-client-id"; + process.env.DATABRICKS_CLIENT_SECRET = "sp-secret"; + await buildModelServingClient({}).getInferenceEndpoint({} as never); + expect(ctorOpts[0].host).toBe("https://envhost.cloud.databricks.com"); + expect((ctorOpts[0].credentials as { name: () => string }).name()).toBe( + "oauth-m2m", + ); + }); + + test("modelServing (asUser) uses the OBO token as PAT, not the SP env creds", async () => { + process.env.DATABRICKS_CLIENT_ID = "sp-client-id"; + process.env.DATABRICKS_CLIENT_SECRET = "sp-secret"; + await buildModelServingClient({ + token: "user-token", + host: "https://x", + }).getInferenceEndpoint({} as never); + expect(ctorOpts[0].credentials).toMatchObject({ + kind: "pat", + token: "user-token", + }); + expect(m2mOpts).toEqual([]); + }); + test("postgres normalizes the host and passes SP M2M credentials", async () => { process.env.DATABRICKS_HOST = "envhost.cloud.databricks.com"; process.env.DATABRICKS_CLIENT_ID = "sp-client-id"; diff --git a/packages/shared/src/workspace-client/types.ts b/packages/shared/src/workspace-client/types.ts index 7a1e0abba..5541fb20d 100644 --- a/packages/shared/src/workspace-client/types.ts +++ b/packages/shared/src/workspace-client/types.ts @@ -21,6 +21,7 @@ import type { ScimClient, GenieClient, JobsClient, + ModelServingClient, FilesClient, TablesClient, VectorSearchClient, @@ -71,6 +72,9 @@ export interface WorkspaceClient extends WorkspaceAuth { /** Statement Execution (modular SDK). */ readonly statementExecution: StatementExecutionClient; + /** Serving endpoints (modular SDK). `servingEndpoints` below stays legacy-typed. */ + readonly modelServing: ModelServingClient; + /** Serving Endpoints. */ readonly servingEndpoints: LegacyWorkspaceClient["servingEndpoints"]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f3964679..82ba5bc69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -299,6 +299,9 @@ importers: '@databricks/sdk-jobs': specifier: 0.57.0 version: 0.57.0 + '@databricks/sdk-modelserving': + specifier: 0.53.0 + version: 0.53.0 '@databricks/sdk-options': specifier: 0.51.0 version: 0.51.0 @@ -653,6 +656,9 @@ importers: '@databricks/sdk-jobs': specifier: 0.57.0 version: 0.57.0 + '@databricks/sdk-modelserving': + specifier: 0.53.0 + version: 0.53.0 '@databricks/sdk-options': specifier: 0.51.0 version: 0.51.0 @@ -2067,6 +2073,10 @@ packages: resolution: {integrity: sha512-LUBLNRhJq0OtUUU6+2T/zRzZMEd4UL0lVm7qTxLIfuDPDNDPy/EHGs3rQ1cXt1Fv0t3Rik7QT/SueZixSsH/Bg==} engines: {node: '>=22.0.0'} + '@databricks/sdk-modelserving@0.53.0': + resolution: {integrity: sha512-MDUmAZ/ajNQZFGyFj8MSvPgBiBK/NsKVQ9QaVjiz6Op5Eik/eJlg3jr+xoSbKDLlR/5ulq/Q2uP5JJA6t3vJTg==} + engines: {node: '>=22.0.0'} + '@databricks/sdk-options@0.51.0': resolution: {integrity: sha512-p5uBh64Y1onnvwlEwfRsmKmGRI23Y4Ly8fZiCgNUwVDe4Z9d0ctI0mo1v4gnDqlmBaBml3TZfqn5BJBROCXsEw==} engines: {node: '>=22.0.0'} @@ -14418,6 +14428,15 @@ snapshots: json-bigint: 1.0.0 zod: 4.3.6 + '@databricks/sdk-modelserving@0.53.0': + dependencies: + '@databricks/sdk-auth': 0.51.0 + '@databricks/sdk-core': 0.51.0 + '@databricks/sdk-options': 0.51.0 + '@js-temporal/polyfill': 0.5.1 + json-bigint: 1.0.0 + zod: 4.3.6 + '@databricks/sdk-options@0.51.0': dependencies: '@databricks/sdk-auth': 0.51.0