Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Publish as model

A deployed Core can be registered as a chat model that any OpenAI-compatible client can reach. This is how a ScaiFlow agent becomes addressable from ScaiBot, custom apps, third-party integrations, or just curl.

The flag#

On the flow's Flow properties panel, check Publish as chat model:

jsonc
"config": {
  "publish_as_model": true,
  "model_visibility_group_ids": ["group_acme_eng", "group_acme_ops"]
}

The compiler surfaces this on the YAML manifest under manifest.publish_as_model and manifest.model_visibility_group_ids. The deploy step reads them and acts.

What happens at deploy#

  1. ScaiFlow's DeployService.deploy_flow first calls POST /v1/modules/scaicore/cores to create the Core (returns core_id).
  2. If publish_as_model is true, it then calls POST /v1/modules/scaicore/cores/{core_id}/publish with optional group_ids as CorePublishRequest.group_ids.
  3. ScaiGrid sets the Core's runtime mode to model and registers a FrontendModel reachable via /v1/chat/completions at slug scaicore/{tenant_slug}/{core.slug}.

The publish call is idempotent — re-deploying with publish_as_model: true updates the registration; toggling it off doesn't currently un-publish (that requires a manual admin call). See Troubleshooting: changing publish state.

Visibility scoping#

The optional model_visibility_group_ids array forwards to ScaiKey-group scoping on the published FrontendModel. Members of any listed group can call the model; others get a 403 from /v1/chat/completions. Leave empty for visibility to the default scope (typically the whole tenant).

Invoking the published model#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
TENANT="acme"
SLUG="customer-support"

curl -X POST "https://scaigrid.scailabs.ai/v1/chat/completions" \
  -H "Authorization: Bearer ${SCAIGRID_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "scaicore/'"$TENANT"'/'"$SLUG"'",
    "messages": [
      {"role": "user", "content": "I'\''d like a refund for order 42"}
    ]
  }'

The Core's entry node receives the request payload as input (mapping varies by entry kind — for entry_api, the payload's input field is what reaches the trigger's emitted scope).

Why this isn't an entry kind#

There is no entry_chat node. The chat surface is owned by ScaiGrid's /v1/chat/completions endpoint, which routes to a Core via its published slug. The Core itself doesn't have a "chat trigger" — its entry is still api or webhook. The OpenAI-compatible layer is a thin shim ScaiGrid manages.

This split keeps the flow graph honest: there's no special chat-shaped block kind in the IR, no risk of authors trying to mix chat-state-machine semantics into a graph that the runtime would silently ignore.

Conversational state (entity mode)#

Stateless Cores (the default) treat every chat completion request independently. For multi-turn conversations with memory, set the Core's instance mode to entity (per-conversation instance) and use the entity_id field on invocations to thread state. This is a ScaiCore-level concern; ScaiFlow doesn't currently expose instance_mode on the canvas — set it via manifest.scaiflow_meta or compile and deploy manually until the canvas picks it up. See ScaiCore: Instance modes.

Updated 2026-05-18 16:05:19 View source (.md) rev 3