Aller au contenu
Console
Try one

    Provisionner des hubs

    Parcourez les compétences de la place de marché, puis créez un groupe de runtime et un hub, installez une compétence et publiez-les depuis votre code.

    Consultez cette page quand votre code doit créer le hub au lieu de passer par le tableau de bord.

    Le provisionnement comporte cinq étapes : découvrir les compétences dans le catalogue de la place de marché, créer un groupe de runtime, créer un hub qui y est rattaché, installer une compétence dans le groupe, puis publier les deux sur un canal. Python, Node.js, Go, Rust, Kotlin, Swift et .NET exposent ces appels, et le serveur MCP les expose sous forme d’outils. Le C embarqué fournit des fonctions d’aide pour le protocole du runtime; provisionnez son hub et son identité de client séparément.

    ÉtapeAppelCe qu’il faut
    Découvrir les compétenceslist_marketplace_skillshubs:read. N’importe quel forfait.
    Créer un groupe de runtimecreate_runtime_grouphubs:write et un forfait payant.
    Créer un hubcreate_hubhubs:write et un forfait payant.
    Installer une compétenceinstall_runtime_group_skillhubs:write, un forfait payant et un accès à la place de marché pour les compétences payantes.
    Publierrelease_runtime_group et release_hubhubs:write et un forfait payant. Les images épinglées doivent être des images que la plateforme publie, sauf si vous êtes administrateur de la plateforme; consultez la ligne platform_image_required dans Guide des erreurs.

    Les noms de méthodes suivent les conventions de chaque langage. Le tableau utilise l’orthographe Python; consultez Noms des appels par langage pour les autres.

    1. Créez un jeton d’API. Créez-le sur la page API Tokens du tableau de bord avec les portées ci-dessous.
    2. Vérifiez le forfait. Toute écriture sur un hub ou un groupe de runtime exige un forfait payant pour l’espace de travail. Parcourir le catalogue, non.
    3. Choisissez le groupe de runtime. Un hub exécute ses compétences dans un groupe de runtime. Créez d’abord le groupe pour que le hub puisse s’y rattacher.
    PortéeCe qu’elle couvre
    hubs:readLe catalogue de la place de marché et la liste des groupes de runtime.
    hubs:inspectL’inventaire d’un groupe de runtime et la vue résolue de la place de marché pour ce groupe.
    hubs:writeLa création, la mise à jour, la publication et la suppression des hubs et des groupes de runtime, ainsi que l’installation de compétences.

    Ces portées s’impliquent les unes les autres. hubs:write accorde hubs:read, qui accorde hubs:inspect. Un jeton créé avec hubs:read couvre tous les appels de découverte de cette page.

    Ce flux découvre des compétences, crée un groupe de runtime et un hub, installe une compétence et publie les deux.

    Les exemples créent un hub Standard. La capacité Autoscaling est un réglage distinct du hub, contrôlé par le forfait de l’espace de travail. Modifier les réglages de réplicas d’un groupe de runtime ne l’active pas.

    import os
    from thalovant import ThalovantControlPlane
    api = ThalovantControlPlane(access_token=os.environ["THALOVANT_API_TOKEN"])
    # 1. Discover what is installable before provisioning anything.
    for skill in api.list_marketplace_skills()["data"]:
    print(skill["skill_id"], skill["title"], skill["access_tier"])
    # 2. Create a runtime group to run the skills.
    group = api.create_runtime_group({"name": "kiosks", "description": "Lobby kiosks"})
    # 3. Create a hub attached to it.
    hub = api.create_hub(
    {
    "name": "joke-garden",
    "runtime_group_id": group["id"],
    "spec": {"protocols": {"wss": {"enabled": True}}},
    }
    )
    # 4. Install a skill from the marketplace catalog.
    api.install_runtime_group_skill(group["id"], "skill-weather")
    # 5. Roll the runtime group and the hub onto a release channel.
    api.release_runtime_group(group["id"], channel="stable")
    api.release_hub(hub["id"], channel="stable")

    Kotlin, Swift et .NET exposent les mêmes cinq appels avec des noms natifs. Leurs pages de SDK montrent la forme propre à chaque langage.

    Les méthodes de compétence adressées à un hub gèrent le groupe de runtime auquel ce hub est rattaché, ce qui touche tous les hubs qui partagent ce groupe. list_hub_skills renvoie les lignes de compétences du hub sous data, chacune avec un installed_version, un update_available et un state; install_hub_skill, update_hub_skill et remove_hub_skill sélectionnent le groupe de runtime à partir de l’identifiant du hub, renvoient une opération dont vous pouvez attendre la fin et s’appliquent à tous les hubs qui partagent ce groupe. La liste exige hubs:inspect; les écritures exigent hubs:write et un forfait payant, et un hub qui n’est pas encore dans un groupe de runtime répond HTTP 404 avec le code hub_without_runtime_group. Les sept SDK gérés fournissent maintenant ces méthodes et l’historique des compétences dans leurs versions actuelles; consultez Ajouter une compétence à un hub pour les routes, le parcours dans l’application et les erreurs.

    Générez et conservez une clé d’idempotence explicite avant la première tentative de création d’un hub. Réutilisez cette clé et le même contenu quand vous réessayez cette même création logique après un délai d’attente réseau dépassé. Sans clé, chaque appel au SDK en génère une nouvelle, de sorte qu’un appel distinct peut créer un deuxième hub. Les SDK ne réessaient pas automatiquement une création. Réutiliser une clé avec un corps différent provoque une erreur; consultez le tableau ci-dessous.

    La création d’un groupe de runtime et l’installation d’une compétence ne prennent pas de clé d’idempotence.

    Une compétence du catalogue n’est pas toujours installable avec votre forfait. La vue de la place de marché du groupe résout le catalogue pour un groupe de runtime précis et indique si chaque entrée peut être installée. Vous pouvez ainsi décider d’avance au lieu de lire une erreur après coup.

    Catalogue

    list_marketplace_skills liste toutes les compétences publiées. Il exige hubs:read et aucun forfait payant.

    Vue de la place de marché du groupe

    list_runtime_group_marketplace résout le catalogue pour un groupe de runtime et indique si chaque compétence est installable et s’il exige un achat. Il exige hubs:inspect.

    Inventaire du groupe

    list_runtime_group_inventory liste les compétences réellement observées en cours d’exécution dans le groupe. Il exige hubs:inspect.

    La mise à jour et la suppression d’un hub utilisent le verrouillage optimiste. Les deux exigent l’etag actuel du hub, que le SDK envoie dans un en-tête If-Match.

    L’etag se trouve seulement dans le corps de la ressource du hub. L’API n’envoie aucun en-tête de réponse ETag. Lisez donc d’abord le hub et prenez le champ etag dans cette réponse.

    hub = api.get_hub(hub_id)
    hub = api.update_hub(hub_id, {"active": False}, etag=hub["etag"])
    api.delete_hub(hub_id, etag=hub["etag"])

    Passez un etag actuel non vide. Les SDK qui offrent une validation locale rejettent les valeurs vides avant l’envoi; un etag périmé atteint l’API et échoue avec HTTP 412 et le détail ETag mismatch. Relisez le hub et réconciliez vos changements avant de réessayer avec son nouvel etag.

    Les écritures sur les groupes de runtime n’utilisent pas d’etag. Les ressources de groupe de runtime ne portent aucun etag à renvoyer, et aucune route de groupe de runtime ne lit If-Match.

    Les anciennes mises à jour de configuration de groupe de runtime par PATCH remplacent la configuration, sauf la section de contrôle protégée. Les clés de runtime omises sont supprimées; personas ne change que si vous le fournissez. Coordonnez les rédacteurs qui utilisent cette route inconditionnelle.

    Pour une mise à jour protégée, lisez GET /v1/runtime-groups/{id}/config, fusionnez vos changements dans son config, puis envoyez PUT au même chemin avec config et expected_revision réglé sur la revision renvoyée. HTTP 412 signifie que rien n’a été écrit : relisez et appliquez de nouveau vos changements d’origine. PUT exige une révision; l’ancien PATCH reste pris en charge. Les anciennes versions de l’API rejettent PUT sans rien écrire.

    Python 0.7.5, Node/Swift/.NET/Kotlin 0.7.1, Go 0.9.1 et Rust 0.9.0, ainsi que MCP 0.5.1, utilisent ce flux protégé par défaut, avec au plus trois tentatives d’écriture. Seul HTTP 412 déclenche une nouvelle lecture et une nouvelle fusion du delta d’origine. Les objets fusionnent de façon récursive; les tableaux et les scalaires sont remplacés. Des personas omis restent inchangés; des personas fournis remplacent les précédents. La fusion exige les portées hubs:read et hubs:write et un forfait payant. Un serveur plus ancien sans révision valide échoue avant toute écriture; il n’y a aucun repli inconditionnel.

    Pour un remplacement complet voulu, utilisez merge=False en Python, merge: false en Node/MCP, ou l’équivalent replaceRuntimeGroupConfig des autres SDK. Le remplacement exige la portée hubs:write, un forfait payant et une coordination avec les autres rédacteurs. Si vous mettez à niveau du code qui comptait sur le remplacement, sélectionnez-le explicitement.

    Le name, le namespace et le domain d’un hub sont fixés à la création. Envoyer une nouvelle valeur pour l’un d’eux échoue avec HTTP 400 et un message comme Name cannot be changed after hub creation. Choisissez-les avec soin : donner un nouveau nom à un hub oblige à créer un nouveau hub et à y reconnecter ses clients.

    Renvoyer la valeur actuelle est accepté. Le champ est retiré de la mise à jour au lieu d’être rejeté, de sorte qu’un cycle lecture-modification-écriture qui transporte ces champs ne se brise pas.

    Les champs modifiables comprennent active, visibility, slug, spec, capacity_profile et le groupe de runtime auquel le hub se rattache. Utilisez le flux avec etag décrit plus haut et n’envoyez que les champs de mise à jour acceptés. Le forfait détermine le plafond de workers quand vous choisissez la capacité Autoscaling.

    Utilisez ce tableau quand vous connaissez déjà la tâche et qu’il vous faut le nom propre au langage.

    TâchePythonNode.jsGoRust
    Lister les compétences du cataloguelist_marketplace_skills(...)listMarketplaceSkills(...)ListMarketplaceSkills(ctx, opts)list_marketplace_skills(opts)
    Créer un hubcreate_hub(payload, ...)createHub(payload, options)CreateHub(ctx, payload, opts)create_hub(payload, idempotency_key)
    Obtenir un hubget_hub(hub_id)getHub(hubId)GetHub(ctx, hubID)get_hub(hub_id)
    Mettre à jour un hubupdate_hub(id, payload, etag=)updateHub(id, payload, { etag })UpdateHub(ctx, id, payload, etag)update_hub(id, payload, etag)
    Supprimer un hubdelete_hub(id, etag=)deleteHub(id, { etag })DeleteHub(ctx, id, etag)delete_hub(id, etag)
    Créer un groupe de runtimecreate_runtime_group(payload)createRuntimeGroup(payload)CreateRuntimeGroup(ctx, payload)create_runtime_group(payload)
    Lister les groupes de runtimelist_runtime_groups(...)listRuntimeGroups(options)ListRuntimeGroups(ctx, ownerID)list_runtime_groups(owner_id)
    Inventaire du groupelist_runtime_group_inventory(...)listRuntimeGroupInventory(...)ListRuntimeGroupInventory(ctx, id, opts)list_runtime_group_inventory(id, refresh)
    Vue de la place de marché du groupelist_runtime_group_marketplace(...)listRuntimeGroupMarketplace(...)ListRuntimeGroupMarketplace(ctx, id, opts)list_runtime_group_marketplace(id, refresh)
    Installer une compétenceinstall_runtime_group_skill(...)installRuntimeGroupSkill(...)InstallRuntimeGroupSkill(ctx, id, skill, opts)install_runtime_group_skill(id, skill, opts)
    Publier un groupe de runtimerelease_runtime_group(...)releaseRuntimeGroup(id, options)ReleaseRuntimeGroup(ctx, id, opts)release_runtime_group(id, opts)
    Publier un hubrelease_hub(...)releaseHub(id, options)ReleaseHub(ctx, id, opts)release_hub(id, opts)
    Lister les compétences d’un hublist_hub_skills(hub_id)listHubSkills(hubId)à venirà venir
    Installer une compétence de hubinstall_hub_skill(hub_id, skill, version=, wait=)installHubSkill(hubId, skill, { version, wait })à venirà venir
    Mettre à jour une compétence de hubupdate_hub_skill(hub_id, skill, version=, wait=)updateHubSkill(hubId, skill, { version, wait })à venirà venir
    Retirer une compétence de hubremove_hub_skill(hub_id, skill, wait=)removeHubSkill(hubId, skill, { wait })à venirà venir

    Kotlin et Swift utilisent l’orthographe de Node.js avec des types d’options natifs, comme createHub et updateHub(id, payload, etag). C# ajoute le suffixe asynchrone, comme CreateHubAsync et UpdateHubAsync(id, options, etag). Les pages des SDK Kotlin, Swift et .NET montrent les signatures exactes.

    StatutDétailCe que cela signifie et quoi faire
    402API access requires a paid plan.L’espace de travail est sur le forfait Free. L’API vérifie ce point avant les portées du jeton. Passez l’espace de travail à un forfait payant, puis réessayez. Les appels de découverte continuent de fonctionner avec le forfait Free.
    403Insufficient scopesLe forfait est payant, mais il manque au jeton la portée qu’exige la route. Créez un jeton avec hubs:write pour le provisionnement, ou hubs:read pour le catalogue.
    402This skill requires paid marketplace access for the tenant plan.Le forfait est payant, mais il ne couvre pas cette entrée payante du catalogue. Consultez d’abord la vue de la place de marché du groupe, qui indique installable, purchase_required et access_message pour chaque compétence.
    412ETag mismatchL’etag If-Match d’une mise à jour ou d’une suppression de hub était absent ou périmé. Relisez le hub, prenez l’etag dans le corps de la réponse et réessayez. Rien n’a été modifié.
    409Idempotency key re-used with different payloadUne création de hub a réutilisé une clé d’idempotence avec un corps différent. Utilisez une nouvelle clé pour un hub réellement nouveau, ou renvoyez exactement le corps d’origine pour rejouer le premier résultat.
    400Name cannot be changed after hub creationLa mise à jour a tenté de modifier name, namespace ou domain. Créez plutôt un nouveau hub.
    403Only an administrator can run an image the platform does not release: …Le code est platform_image_required. Une publication, ou une mise à jour de hub qui modifiait spec.image ou spec.previewBridge.image, a nommé une image que la plateforme ne livre pas. Omettez images pour prendre la publication recommandée, ou épinglez, pour chaque clé d’image, une image épinglée au catalogue du canal stable ou alpha, l’image actuelle ou recommandée de la ressource, l’image de sa politique de publication ou l’image par défaut de la plateforme. Le core d’un groupe de runtime accepte aussi n’importe quelle étiquette ou empreinte de ghcr.io/thalovant/ovos-core, et le listener d’un hub n’importe quelle étiquette ou empreinte de ghcr.io/thalovant/hivemind-listener. bus et preview_bridge n’acceptent que les images listées pour eux. Les champs allowed_images et allowed_repositories de l’erreur indiquent ce que chaque clé refusée peut contenir. Seul un administrateur de la plateforme peut exécuter autre chose.

    La vérification du forfait a lieu avant celle des portées, donc les deux ne se chevauchent jamais. Un appel de provisionnement sur le forfait Free répond 402 API access requires a paid plan., quelles que soient les portées du jeton. Ce message indique la vraie solution : aucune portée qu’un jeton Free peut détenir ne ferait fonctionner l’appel, parce qu’un jeton du forfait Free ne peut jamais porter hubs:write.

    Seul un espace de travail payant atteint la vérification des portées. Là, un jeton sans hubs:write répond 403 Insufficient scopes.

    Lisez un 402 sur un appel de provisionnement comme « cet espace de travail ne peut pas faire cela », et un 403 comme « ce jeton ne peut pas faire cela ». Le seul 403 qui concerne autre chose est platform_image_required : le jeton est correct, c’est l’image qui ne l’est pas. Les deux 402 ont la même forme; distinguez-les par leur texte de détail.

    Le message d’erreur tient sur une ligne et peut être tronqué. Lisez donc plutôt un refus à partir des champs de l’erreur. La phrase de platform_image_required nomme toutes les images que chaque clé refusée peut contenir, et elle est plus longue que la ligne de message de n’importe quel SDK. problem contient le corps complet de l’erreur quand c’est un objet JSON, de sorte qu’un champ que l’API ajoutera plus tard s’y trouvera sans nouvelle version du SDK.

    SDKStatutCodePhrase complèteCorps complet
    Python 0.8.7status_codecodedetailproblem (dict)
    Node.js 0.8.7statusCodecodedetailproblem (objet)
    Go v0.10.6StatusCodeCodeProblemDetailProblem (map[string]any)
    Rust v0.12.0status_code()api_code()api_detail()api_problem() (ApiProblem)
    Kotlin 0.7.17statusCodeerrorCodedetailproblem (JsonObject)
    Swift 0.9.0statusCodeerrorCodedetailproblem (JSONObject)
    .NET 0.8.6StatusCodeErrorCodeDetailProblem (JsonObject)

    Pour platform_image_required, problem contient refused_images, allowed_images (par clé d’image) et allowed_repositories (les clés qui acceptent n’importe quelle étiquette ou empreinte). Pour plan_limit, il contient resource, limit, used et plan.

    from thalovant import ThalovantAPIError
    try:
    api.release_runtime_group(group["id"], images={"core": "docker.io/me/ovos-core:dev"})
    except ThalovantAPIError as error:
    if error.code != "platform_image_required":
    raise
    print(error.detail)
    print(error.problem["allowed_images"])

    Une valeur de votre requête qu’une erreur de validation renvoie reste dans problem et n’atteint jamais le message. Le serveur MCP place le code, la phrase complète et les autres champs dans le texte d’erreur de l’outil; consultez Serveur MCP.

    Le serveur MCP expose le même flux sous forme d’outils. Un agent peut ainsi découvrir des compétences et provisionner un hub sans écrire de code SDK. Les outils de suppression ne sont pas offerts par défaut; consultez Outils destructifs.