Playground Variant Architecture#
The Agenta playground is organized around variants and revisions rather than chat sessions. A variant is a named LLM configuration (model, prompt template, parameters) belonging to an application. Each time a variant's config is saved, a new revision is appended to its history β giving users a full audit trail and the ability to restore any prior configuration.
Core Data Model#
The backend stores this hierarchy in two PostgreSQL tables:
AppVariantDBβ the top-level variant record, holdingvariant_name,config_name,config_parameters(JSONB), and arevisioninteger that tracks the current head.AppVariantRevisionsDBβ the append-only revision history. Each row captures a full snapshot ofconfig_parameters, therevisionnumber, an optionalcommit_message, and audit fields (modified_by_id,created_at,updated_at). The utility methodget_config()returns{"config_name": ..., "parameters": ...}for consumers.
Variants and revisions also carry slug identifiers (URL-safe alphanumerics/underscores/hyphens) via the Slug mixin in the git-layer DTOs , and revisions additionally inherit the Commit mixin with author, date, and message fields .
The newer workflow layer uses parallel generic types β WorkflowVariant and WorkflowRevision β which extend the base Variant/Revision DTOs and field-alias artifact_id as workflow_id for domain clarity. WorkflowRevision.data carries a configuration dict (parameters, variables) and a service dict (URL, schema, kind) that fully describe the revision's runtime behavior .
Revision-Centric Frontend State#
A key architectural decision: the playground treats each revision as a first-class displayable unit, not each variant. When the playground loads, appSchemaMiddleware fetches the variant list, then fetches each variant's full revision history . The adaptRevisionToVariant() function in variant/utils.ts transforms each revision into an EnhancedVariant object, embedding the parent variant's identity in _parentVariant. The playground state's variants array therefore holds revision-level objects, not variant-level objects.
This means selecting "a variant" in the UI is actually selecting a specific revision of that variant. The isLatestRevision flag on EnhancedVariant is determined globally by comparing createdAtTimestamp values across all revisions of all variants .
Two-Phase Loading#
To minimize perceived latency, appSchemaMiddleware uses a two-phase strategy :
- Priority load β extract revision IDs from the URL's
revisionsquery parameter viagetRevisionIdsFromUrl(), fetch only those revisions immediately to unblock rendering. - Background load β queue remaining revisions via
queueMicrotask()without blocking the UI thread.
The EnhancedVariant objects are cached in an out-of-React atomStore (allRevisionsAtom, variantsRefAtom) so they can be accessed across middleware layers without prop-drilling.
usePlayground Middleware Stack#
All playground state flows through usePlayground, which composes six SWR middlewares in order:
| Middleware | Responsibility |
|---|---|
playgroundUIMiddleware | displayedVariants, viewType, toggleVariantDisplay, setDisplayedVariants |
playgroundVariantsMiddleware | Multi-variant orchestration, test runs |
playgroundVariantMiddleware | Per-variant param updates, save operations |
appSchemaMiddleware | Fetches revisions, initializes SWR state |
isVariantDirtyMiddleware | MD5-hash-based unsaved-change tracking |
selectorMiddleware | Scoped data access via variantSelector/stateSelector |
The playground supports a comparison mode: when state.selected holds more than one variant ID, getViewType() returns "comparison" . URL state (?revisions=[...]) is kept in sync by PlaygroundWrapper .
Key API Endpoints#
| Operation | Frontend call | Backend route |
|---|---|---|
| List variants | fetchVariants(appId) | GET /apps/{appId}/variants |
| Get variant revisions | fetchAllPromptVersioning() | GET /variants/{variantId}/revisions |
| Update params | updateVariantParams() | PUT /variants/{variantId}/parameters |
| Fork variant | β | POST /variants/configs/fork |
| Commit changes | β | POST /variants/configs/commit |
| Deploy to env | β | POST /variants/configs/deploy |
Server-side orchestration lives in variants_manager.py, which handles fetch, fork, commit, deploy, and history operations.
Key Source Files#
| File | Role |
|---|---|
api/oss/src/models/db_models.py | AppVariantDB + AppVariantRevisionsDB DB models |
api/oss/src/core/git/dtos.py | Generic Variant + Revision DTOs |
api/oss/src/core/workflows/dtos.py | Workflow-specific variant/revision DTOs |
api/oss/src/services/variants_manager.py | Variant/revision business logic |
api/oss/src/routers/variants_router.py | REST endpoints |
web/oss/src/lib/shared/variant/utils.ts | adaptRevisionToVariant() transform |
web/oss/src/lib/shared/variant/transformer/types/transformedVariant.d.ts | EnhancedVariant type |
web/oss/src/components/Playground/hooks/usePlayground/middlewares/appSchemaMiddleware.ts | Variant/revision fetch + initialization |
web/oss/src/components/Playground/hooks/usePlayground/index.ts | usePlayground middleware chain |
web/oss/src/lib/hooks/useStatelessVariants/state/index.tsx | Jotai atomStore definition |
web/oss/src/services/playground/api/index.ts | Playground-specific API calls |