Downcity
Commands

studio model

Dedicated management for console-global language models (provider/model)

studio model

Dedicated model management command group for language models only.

Design Rules

  • Bare studio model opens an interactive manager in a TTY.
  • create remains a direct shortcut into the create flow.
  • Script subcommands (list/pause/update/test/...) remain available for automation.
  • Provider apiKey is stored in ~/.downcity/downcity.db and encrypted on write.
  • Kimi now has two explicit provider types: moonshot-cn and moonshot-ai.
  • Kimi Code can use a dedicated provider type: kimi-code.

Usage

# Interactive manager
studio model

# Interactive create (choose provider or model)
studio model create

# List
studio model list

# Read details
studio model get provider <providerId>
studio model get model <modelId>

# Pause model (enabled defaults to true)
studio model pause <modelId>
studio model pause <modelId> --enabled false

# Discover provider models (optionally auto-add)
studio model discover <providerId>
studio model discover <providerId> --auto-add true --prefix openai_

# Update provider
studio model update provider <providerId> --api-key "$OPENAI_API_KEY"
studio model update provider kimi_cn --type moonshot-cn
studio model update provider kimi_ai --type moonshot-ai

# Update model
studio model update model <modelId> --name gpt-4o-mini --provider openai_main

# Remove provider/model
studio model remove provider <providerId>
studio model remove model <modelId>

# Bind model to project (set downcity.json.execution.modelId)
studio model use <modelId> --path /path/to/agent

# Test provider
studio model test provider <providerId>

# Test model (real call)
studio model test model <modelId>

Interactive Manager

  • studio model is the human-first entrypoint for the global model pool.
  • It lets you browse providers and models, edit settings, remove entries, test connectivity, pause/resume models, bind a model to a project, and jump into the create flow.
  • Removal is guarded:
    • provider removal is blocked while any model still references it
    • model removal is blocked while any agent project still references it via downcity.json.execution.modelId
  • In non-interactive environments, studio model falls back to normal help output.

create Behavior

  • create is the direct shortcut for creating either a provider or a model.
  • After provider creation, you can immediately run connectivity test.
  • For providers with model discovery support, remote model list can be fetched and auto-added in batch.

Automation Commands

  • get: read a single provider/model detail.
  • discover: fetch provider model list and optionally --auto-add to local pool.
  • remove: remove provider/model.
  • Provider removal is rejected if any model still points at it.
  • Model removal is rejected if any agent project still points at it.
  • use: bind project downcity.json.execution.modelId to a model ID and switch execution mode to api.

pause Behavior

  • pause marks a model as paused.
  • Paused models cannot be loaded as runtime execution.modelId.
  • Useful for incident isolation, cost control, or maintenance windows.

test Behavior

  • test provider: checks provider connectivity and tries model discovery.
  • test model: performs a real model invocation using current config.

Example Output (Based on Current Implementation)

The examples below are aligned with current command implementations in cli/studio/src (including default output mode and field structure). Placeholder values are used for environment-specific fields.

$ studio model list
{
  "success": true,
  "providers": [
    {
      "id": "<providerId>",
      "type": "openai",
      "apiKey": "***masked***",
      "apiKeyMasked": "sk-xx***xxxx"
    }
  ],
  "models": [
    {
      "id": "<modelId>",
      "providerId": "<providerId>",
      "name": "<upstreamModelName>",
      "isPaused": false
    }
  ],
  "providerIds": ["<providerId>"],
  "modelIds": ["<modelId>"]
}
$ studio model pause <modelId>
{
  "success": true,
  "modelId": "<modelId>",
  "isPaused": true
}