ClouisleClouisle

Register Models and Authorize Teams

Register global models, test connections, set capability flags, and authorize teams with quotas and priority

Clouisle models use a two-level structure: an administrator first registers global models on the Models page of the admin panel (provider, endpoint, credentials, capabilities, pricing), then authorizes models to teams with quotas. The platform UI (the Models page and the model selectors in Agents and workflows) only shows models authorized to the current team.

Prerequisite: model egress is gated by the Model Endpoint Allowlist site setting (model_endpoint_allowlist). Custom endpoints (self-hosted gateways, internal Ollama nodes) must be allowlisted first, otherwise saving, testing, and runtime calls are rejected with model_endpoint_not_allowlisted. See Providers, model types, and model discovery.

Registering a global model

In the admin panel > Models, select Add Model, fill in the form, then Test Connection before saving. Writable fields and constraints:

FieldTypeRequiredConstraints and defaultNotes
namestringYes1-100 charactersDisplay name shown in team model lists and selectors
providerenumYesOne of 23 provider identifiersDetermines the adapter and default endpoint
model_idstringYes1-100 charactersReal upstream model ID or deployment name; a typo surfaces as "model not found" at call time
model_typeenumYesOne of 9 typesDetermines the call protocol; must match the provider
provider_display_namestringNo≤ 100 charactersOverrides the user-facing provider name for gateway/proxy setups
base_urlstringNo≤ 512 charactersEmpty falls back to the provider/type default endpoint
api_keystringNo≤ 1024 charactersRequired for every provider except ollama; stored encrypted
context_lengthintegerNo>= 1Used for display and capacity checks only; it does not truncate
max_output_tokensintegerNo>= 1Default output cap, overridable by default_params.max_tokens or a call argument
input_price / output_pricedecimalNo>= 0, max 6 decimal placesPrice per 1M tokens, recorded for reference
default_paramsobjectNo—Default inference parameters, e.g. {"temperature": 0.7, "top_p": 0.9}
capabilitiesobjectNo—Capability flags, see below
configobjectNo—Extra configuration such as Azure api_version/deployment or the video test poll_timeout_s
is_enabledbooleanNoDefault trueGlobal on/off switch
is_defaultbooleanNoDefault falseWhether this is the default model for its model_type
sort_orderintegerNoDefault 0Ascending sort weight

provider, model_id, and model_type are immutable after creation: the update endpoint accepts every field in the table except these three. To change a model ID, provider, or type, create a new model, re-authorize teams, and adjust any knowledge bases or Agents referencing the old one.

Example: registering an OpenAI chat model.

{
  "name": "GPT-4 Turbo",
  "provider": "openai",
  "model_id": "gpt-4-turbo-preview",
  "model_type": "chat",
  "api_key": "sk-...",
  "context_length": 128000,
  "max_output_tokens": 4096,
  "input_price": 10.0,
  "output_price": 30.0,
  "default_params": {"temperature": 0.7, "top_p": 0.9},
  "capabilities": {"streaming": true, "function_call": true}
}

For Azure OpenAI, the deployment name and API version go through config, and model_id holds the deployment name:

{
  "name": "GPT-4 (Azure)",
  "provider": "azure_openai",
  "model_id": "gpt-4-deployment",
  "model_type": "chat",
  "base_url": "https://your-resource.openai.azure.com",
  "config": {"api_version": "2024-02-15-preview", "deployment": "gpt-4-deployment"}
}

Discovering provider models

While the create form is unsaved, you can select Discover Models: after filling in base_url and the API Key, the backend calls the provider's model-list endpoint (such as /v1/models or Ollama's /api/tags) and fills id, name, context length, max output, and capability flags into the form, so you never mistype model_id. 16 providers support discovery; azure_openai, runway, pika, luma, kling, stability, and midjourney do not. Full rules (paths, auth, 15-second timeout, 2 MiB cap, TypeSafe gateway path rebasing) are in Providers, model types, and model discovery.

Credentials and endpoints

  • api_key is stored encrypted by the backend. Every read endpoint returns only has_api_key: true/false and never echoes the plaintext; enter a new value in the edit form to rotate it.
  • API Key format is validated: openai must start with sk-, anthropic with sk-ant-, otherwise the request fails with invalid_api_key_format. ollama is the only provider that needs no API Key.
  • The effective request address resolves as the model's base_url → the type-specific default → the provider default; see Provider identifiers and default endpoints. The chosen endpoint must be an allowlisted Origin.

Capability flags

capabilities holds four flags that drive UI and runtime behavior:

FlagEffectCost of getting it wrong
function_callLets the model call toolsOff: an Agent cannot use tools. On while the model does not support it: protocol errors during tool calls
visionAllows image inputOff: images cannot be used in chat
streamingSupports streaming responsesOff: chat returns in one shot and time-to-first-token grows noticeably
json_modeSupports structured JSON outputOff: JSON-dependent nodes and parameter extraction degrade to text parsing

Before enabling tools for an Agent, confirm the selected chat model truly supports function_call; use Discover Models to read the provider's declaration, then correct it by hand if needed.

Testing connections

Two test endpoints differ in permission and purpose:

EndpointPermissionPurpose
POST /api/v1/admin/models/{model_id}/testadmin:model:updateTests a saved model's actual configuration
POST /api/v1/admin/models/testadmin:model:createTests the configuration in the form before saving; credentials are not persisted

The test drives the real adapter for that model_type with one minimal request:

Model typeTest action
chatSends one chat message whose fixed content is Hi and requires non-empty text back
embeddingEmbeds the string test and requires a non-empty vector
rerankReranks a fixed query against two documents and requires results
decisionSends a TypeSafe decision request with one noul question and requires answers
text_to_imageGenerates one A simple connection test image (openai_responses adds quality: low) and requires image content
text_to_videoCreates a video task and polls until completion using config.poll_timeout_s (default 120 seconds) and config.poll_interval_ms (default 3000)
ttsSynthesizes Hello using default_params.speaker or voice
audio_generationGenerates A short, gentle bell sound
sttValidates only the API Key format; no real transcription is sent

The response is always success, message, and latency_ms. Two things to note:

  • If the upstream returns a rate limit error, every type except text_to_video treats it as "configuration is valid but currently throttled" and still returns success: true with a rate-limit notice — a passing connection test does not mean the quota is sufficient.
  • The video test really creates a task; a polling timeout (default 120 seconds) returns model_test_connection_timeout. Raise poll_timeout_s in config when the provider needs longer.

Not implemented / Roadmap. There is no multi-request performance test (request count, concurrency, p50/p95/p99, cost aggregation). Testing is a single connection/response check.

Enabling, disabling, and default models

  • is_enabled is the model's only status bit: turning it off removes the model from selectors while keeping its configuration and team authorizations, and turning it back on restores them.
  • There is no testing or deprecated state, no deprecation notice, EOL date, or replacement-model suggestion.
  • is_default is unique per model_type: calling POST /api/v1/admin/models/{model_id}/set-default clears the flag on other models of the same type. Knowledge base embedding models and the Agent fallback when no model is specified both rely on it.

Authorizing models to teams

Granting, changing quotas, enabling/disabling a grant, and revoking a grant are superuser-only (the backend handlers depend on get_current_active_superuser). A team administrator who can see the Authorized Models tab still gets a 403 on submit.

  1. Open the admin panel > Teams and open the target team.
  2. Switch to the Authorized Models tab and select Add Model.
  3. Select one or more global models (the dropdown lists only not-yet-authorized, globally enabled models) and submit them together.
  4. Fill in the daily/monthly Token and request limits plus the priority for each grant; leave a field empty for unlimited.
  5. After saving, team members can see the model on their Models page (see below).

An authorization is unique on the team + model pair: authorizing the same model twice returns "already authorized". Batch authorization POST /teams/{team_id}/models/batch only creates new grants and silently skips existing ones; batch revoke DELETE /teams/{team_id}/models/batch returns the number actually deleted.

The Models page a team member sees lists every grant for the team (including disabled ones with is_enabled: false, filterable by status), while model selectors in Agents and workflows only offer grants that are both authorized and enabled (GET /teams/{team_id}/available-models returns only grants that are enabled and whose global model is enabled).

Quotas and usage

All four limits may be null (unlimited), and 0 is a valid value (immediately exhausted):

FieldTypeDefaultMeaning
daily_token_limitinteger / nullnullDaily token cap
monthly_token_limitinteger / nullnullMonthly token cap
daily_request_limitinteger / nullnullDaily request cap
monthly_request_limitinteger / nullnullMonthly request cap

Enforcement is not symmetric, so configure with care:

  • Token limits: rejected only when used + estimated for this call > limit. Reaching exactly the limit does not fail immediately, but any further tokens do.
  • Request limits: rejected when used >= limit, so request number limit still succeeds and request limit + 1 is blocked.
  • Usage resets lazily in the site timezone (TIMEZONE, default Asia/Shanghai): after 00:00 each day and after 00:00 on the 1st of each month, the next quota check zeroes the counters and writes them back.
  • Over quota, the chat endpoint returns business error 6103 with HTTP 429 and the message "model quota exceeded".

GET /teams/{team_id}/models/quota returns each grant's limits, usage, and percentages. Note that is_quota_exceeded accounts only for token limits (used >= limit); compare daily_request_limit with daily_requests_used yourself for request quotas.

Priority

priority defaults to 0 and only affects ordering among models of the same type, never availability. The team model list sorts by -priority, created_at and the available model list by -priority — a larger value moves a model to the front of selectors. Change any limit or the priority in the Edit dialog.

Updating, revoking, and deleting

ActionEffect
Update model (PUT /admin/models/{id})Only name, credentials, endpoint, capabilities, pricing, default parameters, status, and sort order; provider/model_id/model_type cannot change
Disable modelGlobally hidden, team grants kept, restorable at any time
Revoke team grantDeletes the team + model grant and its usage records; the team immediately loses the model
Delete global modelRemoves the global registry entry and cascades to every team's authorization via the foreign key

Deleting a global model cascades to all team authorizations and is irreversible. To retire a model for one team only, revoke that grant or disable it instead of deleting the global model.

Troubleshooting

Connection test fails

  1. Check the allowlist: confirm the custom endpoint's scheme://host:port is in Site Settings > Security > Model Endpoint Allowlist.
  2. Check the credential format: openai needs the sk- prefix and anthropic the sk-ant- prefix; verify the key is not expired and can access the target model.
  3. Check provider/type matching: combinations such as anthropic + embedding or stability + text_to_video fail at call time, not on save.
  4. Check the network: a 15-second timeout and no redirects apply; confirm internal endpoints are reachable and not blocked by a firewall.
  5. Check upstream status: provider throttling or outages show as test failures, but throttling normally still returns success: true.

Quota exhausted (6103 / HTTP 429)

  1. Call GET /teams/{team_id}/models/quota to see which limit was hit.
  2. Raise the limit, switch to a same-type model that has headroom, or wait for the lazy daily/monthly reset.
  3. If you suspect a false positive, check for one oversized request; token limits compare used + estimate.

Slow responses or timeouts

  1. Review the model's context_length and max_output_tokens; an oversized output cap noticeably lengthens responses.
  2. Lower max_tokens, enable streaming (capabilities.streaming), or switch to a faster model.
  3. For video models, check config.poll_timeout_s; the default 120 seconds may be shorter than the provider's render time.

API access

Admin model endpoints live under /api/v1/admin/models and require admin:model:read, admin:model:create, admin:model:update, or admin:model:delete per operation. See the Models API for the endpoint list.

# List models (admin)
models = api.get("/api/v1/admin/models")

# Create a model
model = api.post("/api/v1/admin/models", json={
    "name": "GPT-4 Turbo",
    "provider": "openai",
    "model_id": "gpt-4-turbo-preview",
    "model_type": "chat",
    "api_key": "sk-...",
    "input_price": 10.0,   # per 1M tokens
    "output_price": 30.0
})

# Test the form configuration before saving (credentials not persisted)
api.post("/api/v1/admin/models/test", json={
    "provider": "openai",
    "model_id": "gpt-4-turbo-preview",
    "model_type": "chat",
    "api_key": "sk-..."
})

# Test a saved model
api.post(f"/api/v1/admin/models/{model_id}/test")

# Discover available provider models
api.post("/api/v1/admin/models/discover", json={
    "provider": "openai",
    "base_url": "https://api.openai.com/v1",
    "api_key": "sk-..."
})

# Make it the default model for its type
api.post(f"/api/v1/admin/models/{model_id}/set-default")

# Authorize a team (superuser only) with quotas and priority
api.post(f"/api/v1/teams/{team_id}/models", json={
    "model_id": str(model_id),
    "daily_token_limit": 1_000_000,
    "monthly_token_limit": 20_000_000,
    "daily_request_limit": 2000,
    "monthly_request_limit": 40000,
    "priority": 10
})

# Batch authorize / batch revoke
api.post(f"/api/v1/teams/{team_id}/models/batch", json={
    "model_ids": [str(a), str(b)],
    "monthly_token_limit": 5_000_000
})
api.delete(f"/api/v1/teams/{team_id}/models/batch", json={"model_ids": [str(a)]})

# Inspect quota usage
api.get(f"/api/v1/teams/{team_id}/models/quota")

How is this guide?

On this page