Model Serving plugin
Model Serving plugin
Fournit un proxy authentifié vers les endpoints Databricks Model Serving, avec prise en charge de l'invocation et du streaming.
Fonctionnalités clés :
- Alias nommés pour plusieurs serving endpoints
- Invocation sans streaming (
invoke) et en streaming SSE (stream) - Génération automatique des types OpenAPI pour les schémas de requête/réponse
- Filtrage du corps de la requête selon le schéma de l'endpoint
- Exécution au nom de l'utilisateur (OBO)
Utilisation de base
import { createApp, server, serving } from "@databricks/appkit";
await createApp({
plugins: [
server(),
serving(),
],
});En l'absence de configuration, le plugin lit DATABRICKS_SERVING_ENDPOINT_NAME dans l'environnement et l'enregistre sous l'alias default.
Options de configuration
| Option | Type | Valeur par défaut | Description |
|---|---|---|---|
endpoints | Record<string, EndpointConfig> | { default: { env: "DATABRICKS_SERVING_ENDPOINT_NAME" } } | Table de correspondance entre les alias et les configurations d'endpoint |
timeout | number | 120000 | Délai d'expiration des requêtes, en ms |
Alias d'endpoint
Les alias d'endpoint permettent de référencer plusieurs serving endpoints par leur nom :
serving({
endpoints: {
llm: { env: "DATABRICKS_SERVING_ENDPOINT_NAME" },
classifier: { env: "DATABRICKS_SERVING_ENDPOINT_CLASSIFIER" },
},
})Chaque alias correspond à une variable d'environnement contenant le nom réel de l'endpoint. Si un endpoint dessert plusieurs modèles, vous pouvez utiliser servedModel pour contourner le routage du trafic et cibler directement un modèle précis :
serving({
endpoints: {
llm: { env: "DATABRICKS_SERVING_ENDPOINT_NAME", servedModel: "llama-v2" },
},
})Génération de types
Le plugin Vite appKitServingTypesPlugin() génère des types TypeScript à partir des schémas OpenAPI de vos serving endpoints. Aucune configuration manuelle n'est nécessaire — le serveur de développement AppKit intègre ce plugin automatiquement.
Le plugin détecte automatiquement la configuration des endpoints dans votre fichier serveur (server/index.ts ou server/server.ts).
Les types générés offrent :
- L'autocomplétion des alias, aussi bien côté backend (
AppKit.serving("alias")) que dans les hooks frontend (useServingStream,useServingInvoke) - Des types de requête/réponse/chunk propres à chaque endpoint, basés sur les schémas OpenAPI
Si le schéma OpenAPI d'un endpoint n'est pas disponible (endpoint non déployé, variable d'environnement non définie), le plugin génère des types génériques de repli. L'endpoint reste utilisable — simplement sans typage de la requête ni de la réponse.
Les endpoints qui ne définissent pas de schéma de réponse en streaming dans leur spécification OpenAPI auront chunk: unknown. Pour ces endpoints, utilisez useServingInvoke plutôt que useServingStream — le type response restera correctement typé.
Variables d'environnement
| Variable | Description |
|---|---|
DATABRICKS_SERVING_ENDPOINT_NAME | Nom de l'endpoint par défaut (utilisé lorsque la configuration endpoints est omise) |
Si vous utilisez des endpoints nommés, définissez une variable d'environnement personnalisée pour chaque alias (par exemple DATABRICKS_SERVING_ENDPOINT_CLASSIFIER).
Contexte d'exécution
Par défaut, toutes les routes de serving s'exécutent au nom de l'utilisateur authentifié (OBO), comme pour les plugins Genie et Files. Cela garantit que les permissions CAN_QUERY propres à chaque utilisateur sont bien appliquées sur le serving endpoint.
Pour un accès programmatique via exports(), utilisez .asUser(req) pour exécuter l'appel dans le contexte utilisateur :
// Contexte du service principal (par défaut)
const result = await AppKit.serving("llm").invoke({ messages });
// Contexte utilisateur (recommandé dans les gestionnaires de routes)
const result = await AppKit.serving("llm").asUser(req).invoke({ messages });Endpoints HTTP
Mode nommé (avec la configuration endpoints)
POST /api/serving/:alias/invoke— Invocation sans streamingPOST /api/serving/:alias/stream— Invocation en streaming SSE
Mode par défaut (sans configuration endpoints)
POST /api/serving/invoke— Invocation sans streamingPOST /api/serving/stream— Invocation en streaming SSE
Format de la requête
POST /api/serving/:alias/invoke
Content-Type: application/json
{
"messages": [
{ "role": "user", "content": "Hello" }
]
}Accès programmatique
Le plugin expose les méthodes invoke et stream pour une utilisation côté serveur :
const AppKit = await createApp({
plugins: [
server(),
serving({
endpoints: {
llm: { env: "DATABRICKS_SERVING_ENDPOINT_NAME" },
},
}),
],
});
// Sans streaming
const result = await AppKit.serving("llm").invoke({
messages: [{ role: "user", content: "Hello" }],
});
// Avec streaming
for await (const chunk of AppKit.serving("llm").stream({
messages: [{ role: "user", content: "Hello" }],
})) {
console.log(chunk);
}Hooks frontend
Le package @databricks/appkit-ui fournit des hooks React pour les serving endpoints :
useServingStream
Appel en streaming via SSE :
import { useServingStream } from "@databricks/appkit-ui/react";
function ChatStream() {
const { stream, chunks, streaming, error, reset } = useServingStream(
{ messages: [{ role: "user", content: "Hello" }] },
{
alias: "llm",
onComplete: (finalChunks) => {
// Appelé avec l'ensemble des chunks accumulés à la fin du flux
console.log("Stream done, got", finalChunks.length, "chunks");
},
},
);
return (
<>
<button onClick={stream} disabled={streaming}>Send</button>
<button onClick={reset}>Reset</button>
{chunks.map((chunk, i) => <pre key={i}>{JSON.stringify(chunk)}</pre>)}
{error && <p>{error}</p>}
</>
);
}useServingInvoke
Invocation sans streaming. invoke() renvoie une promesse contenant les données de la réponse (ou null en cas d'erreur) :
import { useServingInvoke } from "@databricks/appkit-ui/react";
function Classify() {
const { invoke, data, loading, error } = useServingInvoke(
{ inputs: ["sample text"] },
{ alias: "classifier" },
);
async function handleClick() {
const result = await invoke();
if (result) {
console.log("Classification result:", result);
}
}
return (
<>
<button onClick={handleClick} disabled={loading}>Classify</button>
{data && <pre>{JSON.stringify(data)}</pre>}
{error && <p>{error}</p>}
</>
);
}Les deux hooks acceptent autoStart: true pour déclencher l'invocation automatiquement au montage.