Skip to content

Consolidate default LLM model names into a single MIT source of truth #2201

Description

@defang-sam

Problem

Default LLM model names live in two places that rot independently, and a retirement in either silently breaks deploys:

  • AWS/GCP — the CLI resolves the alias → concrete model ID statically in src/pkg/cli/compose/fixup.go configureAccessGateway (chat-defaultus.amazon.nova-2-lite-v1:0 / gemini-2.5-flash).
  • Azurepulumi-defang picks from a hardcoded chatPreference list in provider/defangazure/azure/models.go.

This just bit us: Azure's gpt-4o 2024-11-20 entered Deprecating, and defang up on Azure failed with 400 ServiceModelDeprecating. Fixed in pulumi-defang#376, but the fix only patched the Azure list — the two sources of truth remain separate.

Why the two resolution mechanisms must stay different (don't unify the code)

AWS / GCP Azure
Model hosting Serverless (Bedrock / Vertex) — a model ID is just a string; IAM is model-agnostic Must be provisioned as an ARM cognitiveservices.Deployment
Resolution Compile-time, in the CLI (no cloud creds needed) Deploy-time, in the CD task (queries Accounts_ListModels per account+region)
  • "Do it in the CLI for all providers" regresses Azure: the CLI runs before the CD task with no Azure creds and can't know what's deployable in the user's account/region — exactly the blind spot that caused this incident.
  • "Do it in Pulumi for all providers" over-complicates serverless AWS/GCP (adds needless deploy-time catalog queries) and re-plumbs the LiteLLM --model command path.

Proposal: consolidate the data, not the code

Make a single declarative table the source of truth:

{provider, role} → ordered preferred model name(s) + format
  • CLI (AWS/GCP) reads the single top entry and resolves statically as it does today.
  • pulumi-defang (Azure) reads the ordered list as its chatPreference/embeddingPreference seed and keeps the deploy-time lifecycle filter from Implement use of DeleteSecrets cli call. #376 as defense-in-depth (availability is per-account/region; a static list can never be sufficient on Azure alone).

One edit updates every cloud; each provider keeps the resolution mechanism it actually needs.

Where it must live (licensing)

  • pulumi-defang = AGPL-3.0 (viral), defang CLI = MIT.
  • AGPL may depend on MIT; MIT may not depend on AGPL (it would relicense the CLI).
  • ∴ the shared table must be hosted in the CLI (MIT) module (github.com/DefangLabs/defang/src/...) and imported by pulumi-defang — never the reverse.

This is a natural home: the alias vocabulary (chat-default, embedding-default) already lives in the CLI, and the CLI already owns AWS/GCP defaults. The CLI becomes the canonical data owner for all providers even though it doesn't itself deploy Azure.

Sketch

  • Add a leaf package, e.g. src/pkg/cli/compose/models (minimal transitive deps, so pulumi-defang doesn't inherit the CLI's dependency graph), exporting the table.
  • fixup.go configureAccessGateway reads AWS/GCP entries from it.
  • pulumi-defang adds require github.com/DefangLabs/defang/src and seeds the Azure selector from the table.
  • Keep the user-facing alias stable so defang up output is unchanged while the concrete model updates underneath.

Optional follow-up (not required for v1)

Promote the table to Fabric so defaults update via control-plane config without a CLI or CD-image release — this incident required a CD-image rollout to fix. Adds a network hop and new Fabric surface; do it only if release-free updates become a priority.

Non-goals

  • Don't unify the resolution code paths.
  • Don't couple defaults to per-model IAM allowlists (BYOC IAM stays model-agnostic; the Playground GCP :predict allowlist in defang-mvp is a cautionary example, not a pattern to copy).

Acceptance criteria

  • Single declarative default-model table in the CLI (MIT) module.
  • CLI AWS/GCP resolution reads from it (behavior unchanged).
  • pulumi-defang Azure selector seeds from it and retains the lifecycle filter.
  • Updating a default model is a one-line change in one place.
  • Dependency direction is pulumi-defang → defang only.

Context captured from the model-consolidation investigation; see pulumi-defang#376 for the Azure lifecycle fix that motivated this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions