# AWS Architecture Reference Source: https://docs.gp.scale.com/docs/infrastructure/aws/aws-architecture-reference Overview of the SGP AWS architecture ## Architecture Reference ## Cloud Services | Service | Usage | | ------------------------------------ | ------------------------------------------------------------------------ | | Elastic Kubernetes Service (EKS) | SGP applications are deployed to this cluster | | Identity and Access Management (IAM) | Role and policy management for SGP applications access to cloud services | | Secrets Manager | Secret and configuration management for SGP applications | | Key Management Service (KMS) | Encryption of sensitive data at rest | | Simple Storage Service (S3) | Object storage for SGP applications (knowledge bases, models, etc.) | | Simple Queue Service (SQS) | Karpenter spot instance interruption queue | | OpenSearch | Search provider for SGP applications | | Elastic Container Registry (ECR) | Container registry for custom agent images | | Aurora PostgreSQL | Relational databases for SGP applications | | DocumentDB | Document-oriented database for SGP applications | | ElastiCache for Redis | In-memory caching for SGP applications | | CloudWatch | Logging for cloud services | ## Networking SGP is deployed into a VPC spanning three Availability Zones with separate public and private subnets. All application workloads run in private subnets. Outbound internet access is provided by a NAT Gateway. ### Ingress | Property | Default | | -------------------- | ----------------------------------------------------------- | | Load balancer type | Application Load Balancer (ALB) | | Listener port | 443 (TLS) | | Placement | External (internet-facing) | | WAFv2 | Optional — associate an existing Web ACL by name | | VPC Endpoint Service | Optional — enables AWS PrivateLink for private connectivity | The load balancer can be configured as internal (private subnet only) for deployments that require no direct internet exposure. ### Service Mesh Istio is deployed as a cluster add-on and manages all east-west traffic between SGP services. An internal Istio ingress gateway handles traffic routing within the cluster. ## EKS Cluster Default Configuration ### Node Groups | Node Group | Size | Limits | Machine Type | Machine Image | Taints | | ------------ | ---------------------------- | -------- | ------------- | --------------------------------- | ---------------------------------- | | System | 1 | — | m6a.2xlarge | EKS Default AMI | CriticalAddonsOnly=true:NoSchedule | | Default | Karpenter Managed Node Group | 1000 CPU | c\*, m\* | Karpenter Bottlerocket AMI Family | | | High Storage | Karpenter Managed Node Group | 100 CPU | c\*, m\*, r\* | Karpenter Bottlerocket AMI Family | | | GPU | Karpenter Managed Node Group | 385 CPU | g5.\* | Karpenter Bottlerocket AMI Family | nvidia.com/gpu:NoSchedule | | GPU (Large) | Karpenter Managed Node Group | 100 CPU | p4d.\* | Karpenter Bottlerocket AMI Family | nvidia.com/gpu:NoSchedule | ### Cluster Add-ons | Add-on | Purpose | | ---------------------------- | ------------------------------------------------------------------ | | Karpenter | Workload-driven node autoscaling for Karpenter-managed node groups | | Cluster Autoscaler | Scaling for the system node group | | Istio | Service mesh for east-west traffic management | | cert-manager | TLS certificate provisioning and renewal | | AWS Load Balancer Controller | Kubernetes-native ALB and NLB provisioning | | external-dns | Automatic Route 53 DNS record management | | AWS Node Termination Handler | Graceful pod drain on spot interruption or node termination | | NVIDIA GPU Operator | GPU driver and device plugin lifecycle management | | AWS EBS CSI Driver | Persistent volume support for EKS workloads | ## Aurora PostgreSQL Default Configuration | Instance | Service | Instance Count | Engine | Instance Class | | ----------------------------------------------------- | --------------------------------- | -------------- | ------------ | -------------- | | agents-service-\ | Agents | 2 | PostgreSQL16 | db.r6g.xlarge | | duc-\-duc-api-aurora-db | Document Understanding Capability | 3 | PostgreSQL15 | db.r6g.large | | reducto-\-aurora-db | Reducto | 3 | PostgreSQL15 | db.r6g.large | | sae-\-sae-postgres | Semantic Analysis Engine | 1 | PostgreSQL17 | db.r6g.large | | scale-egp-\-agentex-aurora-db | Agentex | 3 | PostgreSQL15 | db.r6g.large | | scale-egp-\-egp-api-aurora-db | EGP API | 3 | PostgreSQL15 | db.r6g.large | | scale-egp-\-identity-service-aurora-db | Identity Service | 3 | PostgreSQL15 | db.r6g.large | | scale-egp-\-llm-engine-aurora-db | LLM Engine | 2 | PostgreSQL14 | db.r6g.large | | scale-egp-\-spicedb-postgres | SpiceDB | 3 | PostgreSQL16 | db.r6g.large | | scale-egp-\-temporal-aurora-db | Temporal | 3 | PostgreSQL15 | db.r6g.large | | scale-egp-\-train-aurora-db | SGP Train | 3 | PostgreSQL15 | db.t3.medium | ## DocumentDB Default Configuration | Instance | Service | Instance Count | Instance Class | | ---------------------------------------- | ------- | -------------- | -------------- | | scale-egp-\-compass-docdb | Compass | 1 | db.t3.medium | | \-agentex-mongo | Agentex | 1 | db.r5.large | ## Customer Sensitive Data Sensitive data may be stored in the following services, all data is encrypted at rest and in transit: * Aurora PostgreSQL: For storing metadata, persistent data, and service-specific databases (SGP API Backend, Identity Service, LLM Engine, SpiceDB, Agents Service, SGP Train, and Temporal) * DocumentDB: For storing Agentex agent data/workflows and Workflows document processing state * S3 Buckets: For storing large data objects and datasets * OpenSearch: For managing text embeddings and vector search capabilities # AWS SGP Deployment Source: https://docs.gp.scale.com/docs/infrastructure/aws/aws-sgp-deployment End-to-end guide to deploying SGP in an AWS account. ## Overview This guide will walk you through the process of deploying SGP in an AWS account. SGP AWS infrastructure is defined by terraform modules managed by Scale. ## Prerequisites * Access to an AWS account with sufficient permissions to create resources * The following tools installed: * [AWS CLI](https://aws.amazon.com/cli/) * [Terraform](https://www.terraform.io/) * [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) * The following from Scale: * The SGP AWS Infrastructure Terraform modules * A `workspace_id` and `registration_secret` unique to your deployment * `install.sh` script to bootstrap the SGP cluster * A new application configured in your identity provider to authenticate to the SGP platform (SAML or OIDC) (optional) * A custom domain for your deployment (optional) ## Installation ### Step 1: Prepare Terraform Root Module In a new directory, create a `.tf` file to invoke the SGP AWS infrastructure Terraform modules. Reference the `egp-hybrid-cloud-customer-deployment` module as the entrypoint for the SGP infrastructure. ```terraform theme={null} module "egp_hybrid_deployment" { source = "" scale_account_id = "" registration_secret = "" # sensitive — use TF_VAR_registration_secret env var domain = ".workspace.egp.scale.com" # or your custom domain # Core services — enable the components you need deploy_egp_api = true # EGP API backend — required for the SGP platform deploy_spellbook = false # Spellbook prompt management service (optional) deploy_sgp_models = false # SGP model serving infrastructure (optional) deploy_llm_engine = false # Model Engine service (optional) deploy_agentex = false # Agentex agentic workflow service (optional) create_agentex_tool_cache = false # Persistent tool cache for Agentex (optional) enable_cloud_build = false # Cloud-based container build service for Agentex (optional) add_agents_service = false # Agent Service for building and running agents (optional) add_document_understanding_capability = false # Document Understanding (Dex) service (optional) enable_reducto = false # Reducto OCR and document parsing service (optional) enable_train = false # SGP model training infrastructure (optional) enable_compass = false # Workflows workflow orchestration service (optional) enable_ukbp = false # Universal Knowledge Base Pipeline (optional) enable_system_manager = true # System Manager deployment orchestrator — required enable_monitoring = false # Observability stack (optional) create_temporal_postgresql = true # Recommended; set false to use an in-cluster Cassandra instead omit_egp_control_plane = false # Set true to disable communication with Scale's hosted control plane base_repository = "" # Only needed if not pulling from Scale's registry openai_api_key = "" # sensitive — use TF_VAR_openai_api_key env var openai_org_id = "" anthropic_api_key = "" # sensitive — use TF_VAR_anthropic_api_key env var launch_api_key = "" # sensitive — use TF_VAR_launch_api_key env var authentication_type = "SAML" # "SAML" or "OIDC" use_cmk = true # Recommended for production: encrypts data at rest using AWS KMS # If deploying into an existing VPC rather than creating a new one, provide the IDs here # vpc_id = "" # private_subnet_ids = ["", ""] # public_subnet_ids = ["", ""] access_cidrs = [] # List of CIDR blocks to allow access; defaults to open to internet # Load balancer configuration load_balancer_type = "application" # "application" or "network" # waf_name = "" # Optional; only applies to application load balancers internal_lb = false # Set true to prevent internet access to the load balancer # additional_certs = [""] # Additional TLS certificates for the load balancer # permissions_boundary_arn = "" # Optional: IAM permissions boundary for provisioned roles # See the module documentation for the full list of available variables } # Example Terraform provider and backend configuration provider "aws" { region = "" } terraform { required_version = ">= 1.1.7" backend "s3" { bucket = "sgp--terraform-state" key = "sgp--sgp.tfstate" region = "" } } ``` ### Step 2: Provision Infrastructure via Terraform Run the following commands in the same directory as the previous step's .tf file. ```bash theme={null} terraform init terraform plan -out=tfplan # Inspect this plan to review the planned resources to be provisioned before proceeding terraform show tfplan terraform apply tfplan ``` *This step may take significant time due to resource creation dependencies* If a pre-created certificate and domain entry are not provided, you will likely encounter the following error: ```bash theme={null} Error: creating ELBv2 Listener (arn:aws:elasticloadbalancing:us-east-1:12345678910:loadbalancer/net/scale-egp-/12345678910): operation error Elastic Load Balancing v2: CreateListener, https response error StatusCode: 400, RequestID: 12345678910, api error UnsupportedCertificate: The certificate 'arn:aws:acm:us-east-1:12345678910:certificate/12345678910' must have a fully-qualified domain name, a supported signature, and a supported key size. ``` Resolve this by properly configuring DNS and TLS for your domain before re-running `terraform apply`. ### Step 3: Bootstrap the Cluster Note the following values from the previous step's apply output: ```bash theme={null} export SYSTEM_MANAGER_IRSA_ROLE_ARN=$(terraform output -raw system_manager_irsa_role_arn) export SPICEDB_DATASTORE_URI=$(terraform output -raw spicedb_connection_string) export AWS_REGION="" ``` #### Configure EKS access Configure access for yourself to the provisioned EKS cluster: 1. Navigate to "Elastic Kubernetes Service" on the AWS console 2. Go to the required cluster 3. Go to *Access* 4. Under *IAM access entries* click *Create Access Entry* 5. Select your current AWS IAM role from the dropdown, leave the *Type* as *Standard* 6. Add the `AmazonEKSAdminPolicy` and `AmazonEKSClusterAdminPolicy` to the *Cluster* scope 7. Click create #### Allowlist your IP (if needed) 1. Navigate to "Elastic Kubernetes Service" on the AWS console 2. Go to the required cluster 3. Go to *Networking* 4. On the right, click *Manage* and click *Endpoint Access* 5. Under *Advanced settings* add your IP address #### Execute install.sh Run the `install.sh` script to create the following Kubernetes resources: * AWS Secret Manager CSI Driver * Spicedb * [SGP System Manager](/docs/infrastructure/system-manager) ```bash theme={null} cd # Set the correct kubectl context aws eks update-kubeconfig --name scale-egp- --region # Execute the install.sh script ./install.sh ``` System Manager is the deployment orchestrator for the SGP platform. It will automatically begin deploying the other services required for the SGP platform within the cluster. ### Step 4: Verify the Deployment Wait for all services to be ready: ```bash theme={null} kubectl get helmreleases -A # All should be Ready=True kubectl get pods -A # All should be Running ``` ### Step 5: Configure Identity Provider #### SAML Configuration In your Identity Provider, configure a new SAML application with the following settings: * Service Entity ID: `https://auth.` * Redirect URI: `https://auth./dashboard/org/saml/callback` Modify the configuration `scale-egp-/identity-service-saml-secrets` (either via System Manager or directly in AWS Secrets Manager): ```json theme={null} { "id": "", "samlConfiguration": { "entityId": "", "x509Cert": "", "ssoUrl": "", "attributeMappings": { "email": "", "firstName": "", "lastName": "" } } } ``` #### OIDC Configuration In your Identity Provider, configure a new OIDC application with the following settings: * Redirect URI: `https://auth./dashboard/org/oidc/callback` Modify the configuration `scale-egp-/identity-service-oidc-secrets` (either via System Manager or directly in AWS Secrets Manager) with the following JSON: ```json theme={null} { "id": "", "oidcConfiguration": { "clientId": "", "clientSecret": "", "issuer": "", "authorizationUrl": "", "tokenUrl": "", "userInfoUrl": "" } } ``` After modifying the secret, restart the `sgp-system-manager` deployment to apply the changes. ```bash theme={null} kubectl rollout restart deployment sgp-system-manager -n sgp-system-manager ``` ## Accessing the Platform If all goes smoothly, you should be able to navigate to the SGP platform at `https://.workspace.egp.scale.com` (or your custom domain) and authenticate via the configured identity provider. # Azure Architecture Reference Source: https://docs.gp.scale.com/docs/infrastructure/azure/azure-architecture-reference Overview of the SGP Azure architecture ## Architecture Diagram ```mermaid theme={null} graph TD USER(["Client"]) INTERNET(["Internet"]) subgraph AZ["Azure Subscription"] FD["Azure Front Door
WAF · CDN · TLS termination"] subgraph VNET["Virtual Network — 10.0.0.0/16"] INGRESS["Istio Ingress
Internal Load Balancer"] NAT["NAT Gateway
Outbound egress"] subgraph AKS["AKS Cluster — private API server"] SYS["System Node Pool
3× Standard_D4s_v3"] APP["User Node Pool
3–10× Standard_D16s_v3"] end BASTION["Azure Bastion"] JUMP["Jump Host VM"] end KV["Key Vault
Secrets + CMK keys"] LAW["Log Analytics Workspace"] PG[("PostgreSQL
Flexible Server")] REDIS[("Redis Cache
Premium")] STOR[("Storage Account")] SB[("Service Bus
Premium")] end USER --> FD FD -->|"Private Link"| INGRESS INGRESS --> APP APP -->|"Private Endpoint"| PG APP -->|"Private Endpoint"| REDIS APP -->|"Private Endpoint"| STOR APP -->|"Private Endpoint"| SB APP --> KV SYS --> KV APP --> LAW SYS --> LAW APP --> NAT --> INTERNET BASTION --> JUMP -->|"kubectl"| AKS ``` *** ## Resources by Type ### Compute Resources | Resource | Count | Purpose | | ---------------- | ----- | ---------------------------- | | AKS Cluster | 1 | Kubernetes orchestration | | System Node Pool | 1 | System pods (fixed 3 nodes) | | User Node Pool | 1 | Application workloads | | GPU Node Pool | 0-1 | AI/ML workloads (optional) | | Cassandra Pool | 0-1 | Temporal database (optional) | **Total VMs:** 6-24 (3 system + 3-10 user + 0-5 GPU + 0-6 Cassandra) *** ### Network Resources | Resource | Count | Purpose | | ----------------- | ----- | ------------------------ | | Virtual Network | 1 | Network boundary | | Subnets | 4-5 | Network segmentation | | NSGs | 4-5 | Traffic control | | Route Tables | 3-4 | Traffic routing | | Private DNS Zones | 7 | Internal name resolution | | Private Endpoints | 7 | Secure PaaS access | | Bastion Host | 0-1 | Secure VM access | | Public IPs | 0-1 | Bastion endpoint | *** ### Data & Storage Resources | Resource | Count | Purpose | | ----------------- | ----- | --------------------------- | | PostgreSQL Server | 1 | Relational database | | Redis Cache | 1 | Distributed cache | | Storage Account | 1 | Blob/File storage | | AI Search Service | 0–1 | Full-text search (optional) | | OpenAI Service | 0–1 | LLM models (optional) | *** ### Security Resources | Resource | Count | Purpose | | --------------------- | ----- | ---------------------- | | Key Vault | 1 | Secrets management | | Managed Identity | 1-2 | Service authentication | | RBAC Role Assignments | 10+ | Access control | *** ### Monitoring Resources | Resource | Count | Purpose | | ----------------------- | ----- | ------------------- | | Log Analytics Workspace | 1 | Centralized logging | | Data Collection Rule | 1 | AKS metrics | | Diagnostic Settings | 7+ | Resource logging | | Datadog Connection | 0-1 | External monitoring | *** ## Network Architecture ### Address Space Planning ``` VNet: 10.0.0.0/16 (65,536 IPs) ├── AKS Subnet: 10.0.1.0/24 (256 IPs) ├── Bastion Subnet: 10.0.2.0/26 (64 IPs) ├── Database Subnet: 10.0.3.0/24 (256 IPs) └── Private Endpoints: 10.0.4.0/25 (128 IPs) Pod CIDR: 10.244.0.0/16 (65,536 IPs) Service CIDR: 10.243.0.0/16 (65,536 IPs) ``` ### Traffic Flow **Egress (Internet):** ``` Pods/VMs → NAT Gateway → Public IP → Internet (Stateful, return traffic allowed) ``` **Ingress (Internal):** ``` Service IP → Load Balancer → Pod IP (via CNI) ``` **Database Access:** ``` AKS Pods → Private Endpoint → Private Link → PostgreSQL (DNS: server.postgres.database.azure.com) ``` **External Service Access:** ``` AKS Pods → API Gateway / Load Balancer → OpenAI / AI Search (Via Private Endpoints) ``` *** ## AKS Configuration Deep Dive ### API Server Access **Type:** Private cluster (recommended) **Endpoint:** Internal only **Access Method:** Bastion host or VPN **DNS:** k8s..azmk8s.io (private) ### Network Policies **Engine:** Azure Network Policy **Scope:** Pod-to-pod communication **Default:** Allow all (unrestricted) **Configuration:** Define in Kubernetes manifests ### Container Registry **Integration:** Azure Container Registry (optional) **Authentication:** Managed identity or pull secrets **Pulling:** Private endpoint (optional) ### Monitoring & Observability **Azure Monitor Agent:** Deployed in kube-system **Metrics:** CPU, Memory, Disk, Network **Logs:** Container stdout/stderr, Kubernetes events **Dashboards:** Pre-built in Log Analytics # Azure SGP Deployment Source: https://docs.gp.scale.com/docs/infrastructure/azure/azure-sgp-deployment End-to-end guide to deploying SGP in an Azure subscription. ## Overview This guide walks you through deploying SGP in an Azure cloud subscription using the SGP Azure Terraform modules. SGP Azure infrastructure is defined by terraform modules managed by Scale. ## Prerequisites * Access to an Azure subscription with sufficient permissions to create resources (Contributor + User Access Administrator roles, or equivalent) * The following tools installed: * [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) (`az login` completed) * [Terraform](https://www.terraform.io/) (`>= 1.1.7, < 2.0.0`) * [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) * The following from Scale: * The SGP Azure Infrastructure Terraform modules (`azure-terraform/infra`) * A `workspace_id` and `registration_secret` unique to your deployment * A new application configured in your identity provider to authenticate to the SGP platform (SAML or OIDC) (optional) * A custom domain for your deployment (optional) ## Installation ### Step 1: Build Configuration Configuration is split across three files. This allows you to reuse the same configuration for multiple environments. ``` infra/ ├── main.tfvars.json # Selects which domain + environment to deploy └── config/ └── / ├── default.yaml # Domain-wide defaults (all environments) └── / └── .yaml # Environment-specific overrides ``` #### File 1: `main.tfvars.json` This file selects which configuration to load. Edit it to point at your customer and environment before running Terraform. ```json theme={null} { "environment_type": "", "domain_code": "", "location": "" } ``` | Field | Description | Allowed values | | ------------------ | ---------------------------------------------------- | ---------------------------------------------- | | `environment_type` | Environment tier | `dev`, `qa`, `staging`, `uat`, `prod` | | `domain_code` | Customer identifier (3–9 alphanumeric chars/hyphens) | e.g. `acme`, `contoso` | | `location` | Azure region | e.g. `eastus2`, `northeurope`, `southeastasia` | #### File 2: `config//default.yaml` Customer-wide baseline settings that apply to **all** environments unless overridden. This file is the right place for stable policy and posture decisions: tenant identity, tagging standards, and security baselines for Key Vault, PostgreSQL, Redis, Storage, and other services. ```yaml theme={null} # Identity business_unit: "" tenant_id: "" default_tags: costcenter: "" product: "" deploymenttype: "new" safe-to-delete: "no" # CMK key names (Terraform creates these keys when cmk.create=true in the env YAML) cmk: create: true encryption_key_name: "encryptionCMK" k8s_key_name: "k8sEncryptionCMK" service_bus_key_name: "serviceBusEncryptionCMK" # Key Vault network posture — secure-by-default baseline. # Override public_network_access_enabled and network_acls_ip_rules in your env YAML # if you need temporary public access during bootstrap from a laptop or CI runner. keyvault: public_network_access_enabled: false enable_rbac_authorization: true sku_name: "standard" soft_delete_retention_days: 90 # Cannot be changed after vault creation purge_protection_enabled: true network_acls_default_action: "Deny" network_acls_bypass: "AzureServices" network_acls_ip_rules: [] # PostgreSQL baseline psql: public_network_access_enabled: false sku_name: "GP_Standard_D2s_v3" psql_version: "15" backup_retention_days: 7 bootstrap_aad_principals: false auth: active_directory_auth_enabled: true password_auth_enabled: true storage_mb: 32768 storage_tier: "P10" auto_grow_enabled: true azure_extensions: "uuid-ossp,ltree" # Redis baseline redis: public_network_access_enabled: false sku_name: "Premium" family: "P" capacity: 1 active_directory_authentication_enabled: true access_policy_name: "Data Owner" non_ssl_port_enabled: false tls_version: 1.2 redis_version: 6 # Storage account baseline storage_account: public_network_access_enabled: false min_tls_version: "TLS1_2" account_tier: "Standard" account_replication_type: "LRS" allow_nested_items_to_be_public: false # OpenAI — leave client.mode empty to let Terraform infer from open_ai.create open_ai: client: mode: "" key: "" org_id: "" custom_url: "" # NAT Gateway baseline nat_gateway: sku_name: "Standard" public_ip_sku: "Standard" public_ip_allocation_method: "Static" zones: ["1", "2", "3"] idle_timeout_in_minutes: 10 # AKS baseline aks: load_balancer_sku: "standard" only_critical_addons_enabled: true key_vault_secret_rotation_enabled: true # Front Door baseline frontdoor: create: true ssl_mode: managed minimum_tls_version: "TLS12" forwarding_protocol: HttpOnly private_link_service_name: "sgp-ingress-lb" subdomains: ["auth", "api", "@", "admin"] extra_subdomains: [] dns_ttl_seconds: 60 ip_filtering: enabled: false allowed_ip_ranges: [] # System Manager bootstrapping baseline bootstrapping: az_cli_version: "2.59.0" helm_chart_version: "2.1.0" base_repository: "" ``` #### File 3: `config///.yaml` Environment-specific configuration. This is where you set everything that differs per environment: resource names, subscription, CIDRs, feature flags, node pool sizing, and bootstrapping state. ```yaml theme={null} # ── Environment identity ─────────────────────────────────────────────────────── location: eastus2 subscription_id: "" name_suffix: "" # e.g. "acme01" deployment_id: "" deployment_url: ".workspace.egp.scale.com" temporal_db_mode: postgresCommonHosted # Recommended; or "cassandraK8s" (not recommended) # ── Auth / secrets ──────────────────────────────────────────────────────────── authType: default # Options: default, SAML, OIDC RBAC: false registration_secret: "" # Auto-generated if empty identity_service_jwt: "" # Auto-generated if empty # ── OIDC (required only when authType = OIDC) ───────────────────────────────── oidc: clientId: "" clientSecret: "" issuer: "" authorizationUrl: "" tokenUrl: "" userInfoUrl: "" # ── SAML (required only when authType = SAML) ───────────────────────────────── saml: X509Cert: "" SSOUrl: "" emailAttrName: "" firstNameAttrName: "" lastNameAttrName: "" # ── SSL ─────────────────────────────────────────────────────────────────────── sslMode: e2e # Options: e2e, managed # SSL cert/key for Istio ingress (PEM file paths or base64). Required when sslMode=e2e. istio: sslCert: "" sslKey: "" # ── Resource group + networking ─────────────────────────────────────────────── resource_group: "rg-eus2---dev-01" network: vnet: "sgpaz-vnet" int_rt_name: "sgpaz-rt-int" int_nsg_name: "sgpaz-nsg-int" private_cluster: true nsg_rules: postgresql: true redis: true private_dns: create: true private_cluster: true subnets: psql: name: "sgpaz-snet-psql" type: int address_prefixes: - "" # e.g. "10.95.23.160/27" service_endpoints: - Microsoft.Storage service_delegation: name: Microsoft.DBforPostgreSQL/flexibleServers actions: - Microsoft.Network/virtualNetworks/subnets/join/action deployment_scripts: name: "sgpaz-snet-deployscripts" type: int address_prefixes: - "" service_delegation: name: Microsoft.ContainerInstance/containerGroups actions: - Microsoft.Network/virtualNetworks/subnets/action aks: name: "sgpaz-snet-aks" type: int address_prefixes: - "" private_endpoint_network_policies: Disabled service_endpoints: - Microsoft.CognitiveServices bastion: name: AzureBastionSubnet type: int address_prefixes: - "" jump_host: name: "sgpaz-snet-jumphost" type: int address_prefixes: - "" # ── Workload Identity + NAT Gateway ─────────────────────────────────────────── workload_identities: enabled: true nat_gateway: enabled: true name: "sgpaznat" zones: [] # [] = regional (no zone pinning) # ── CMK ─────────────────────────────────────────────────────────────────────── # Set create=true for new deployments. If CMK keys already exist, set create=false # and provide the key URIs to avoid "already exists (import required)" errors. cmk: enabled: true create: true # encryption_key_uri: "https://.vault.azure.net/keys/encryptionCMK/" # k8s_key_uri: "https://.vault.azure.net/keys/k8sEncryptionCMK/" # service_bus_key_uri: "https://.vault.azure.net/keys/serviceBusEncryptionCMK/" # ── Key Vault ───────────────────────────────────────────────────────────────── # Override public access settings here if bootstrapping from a laptop or CI runner. keyvault: name: "sgpazkeyvault" # Must be globally unique; 3-24 characters soft_delete_retention_days: 90 # Must match existing vault if already created public_network_access_enabled: true # Set false once stable; keep true during bootstrap network_acls_ip_rules: - "/32" # Your laptop or CI runner IP # ── Core services ───────────────────────────────────────────────────────────── law: create: true psql: create: true name: "sgpazpostgres" # Must be globally unique admin_user: postgres bootstrap_aad_principals: true # Runs once after cluster is up; requires kubectl access redis: create: true name: "sgpazredis" # Must be globally unique storage_account: create: true name: "sgpazstorage" # Must be globally unique; no hyphens enable_file_private_endpoint: true service_bus: create: true name: "sgpazservicebus" sku: Premium # Premium required for private endpoints capacity: 1 premium_messaging_partitions: 1 private_endpoint_enabled: true public_network_access: false # ── AI services ─────────────────────────────────────────────────────────────── # Modes: use_openai_via_azure | use_openai_via_custom_endpoint | no_openai # NOTE: Do not commit real API keys. Keep `key` as a local uncommitted change. open_ai: create: false name: "sgpazopenai" client: mode: "use_openai_via_custom_endpoint" key: "" custom_url: "" ai_search: create: false name: "sgpazaisearch" # ── AKS ─────────────────────────────────────────────────────────────────────── aks: create: true name: "sgpazaks" node_resource_group: "rg-aks-eus2---dev-01" dns_prefix: "egp-k8scluster" sku: Free # Free | Standard (use Standard for production) kubernetes_version: "1.30" run_command_enabled: true enable_encryption_at_host: true use_azure_managed_flux: true zones: ["1", "2", "3"] istio: enabled: true ingress_mode: External revisions: - asm-1-20 node_pools: system: name: default vm_size: Standard_D4s_v3 count: 3 min_count: 3 max_count: 6 user: enabled: true name: user vm_size: Standard_D16s_v3 count: 3 min_count: 3 max_count: 10 gpu: enabled: false # Set true to enable GPU workloads name: gpu vm_size: Standard_NV72ads_A10_v5 min_count: 0 max_count: 5 cassandra: enabled: false # Required only if temporal_db_mode is cassandraK8s network: pod_cidr: "10.244.0.0/16" service_cidr: "10.243.0.0/16" dns_service_ip: "10.243.0.10" outbound_type: userDefinedRouting # ── Bastion / jump host ─────────────────────────────────────────────────────── bastion: create: true admin_username: azureuser ssh_public_key: "" # ── Front Door overrides ────────────────────────────────────────────────────── # Most Front Door settings inherit from default.yaml. Override only what differs. frontdoor: extra_subdomains: [] # e.g. ["chat"] if Agentex UI is enabled # ── Feature flags ───────────────────────────────────────────────────────────── feature_flags: models: false agentex: create: false # Set true to provision Agentex service compass: create: false # Set true to provision Workflows service dex: create: false # Set true to provision Dex (Document Understanding) service reducto: create: false # Set true to provision Reducto service # ── Bootstrapping (System Manager) ──────────────────────────────────────────── bootstrapping: enabled: true use_managed_flux: true system_manager_version: "" desiredState: | { "version": "0.1", "packs": [ { "name": "cert-manager" }, { "name": "egp" }, { "name": "identity-service" }, { "name": "spicedb" }, { "name": "sgp-apps" }, { "name": "sgp-models" } ] } # ── Policy assignments ──────────────────────────────────────────────────────── policy_assignments: enabled: false # Set true to enforce Azure Policy tag rules # ── Observability ───────────────────────────────────────────────────────────── observiqidp: monitoring: enabled: false insights: enabled: false ``` **Naming constraints for Azure resources:** * Key Vault names: 3–24 alphanumeric characters and hyphens, globally unique * Storage Account names: 3–24 lowercase letters and numbers only (no hyphens), globally unique * PostgreSQL and Redis names: globally unique within Azure * All names must remain stable after first apply — many Azure resources cannot be renamed ### Step 2: Provision Infrastructure via Terraform Navigate to the `infra` directory and initialize: ```bash theme={null} cd infra terraform init ``` Review and apply the plan using the `main.tfvars.json` file you configured in the previous step: ```bash theme={null} terraform plan -var-file=main.tfvars.json -out=tfplan # Inspect the plan to review resources before proceeding terraform show tfplan terraform apply tfplan ``` If the Datadog monitoring integration is enabled in your YAML, pass the API key via environment variable rather than committing it to file: ```bash theme={null} TF_VAR_datadog_api_key="" terraform plan -var-file=main.tfvars.json -out=tfplan ``` *This step may take significant time (30–60 minutes) due to resource creation dependencies, particularly the AKS cluster and PostgreSQL Flexible Server.* ### Step 3: Bootstrap the Cluster The Azure infrastructure **automatically bootstraps** [SGP System Manager](/docs/infrastructure/system-manager) via an Azure Deployment Script. When `bootstrapping.enabled: true` is set in your configuration, Terraform provisions an Azure Container Instance that: 1. Installs Flux CD on the AKS cluster (using Azure Managed Flux if `use_managed_flux: true`) 2. Applies the System Manager `HelmRepository` and `HelmRelease` Flux CRDs 3. Waits for System Manager to reconcile Monitor bootstrap progress in the Azure Portal under **Deployment Scripts** in your resource group, or check System Manager logs after bootstrap: ```bash theme={null} # Get AKS credentials (from inside the VNet or via Bastion — cluster is private by default) az aks get-credentials \ --resource-group \ --name sgpazaks \ --overwrite-existing # Verify System Manager is running kubectl get pods -n sgp-system-manager # Watch System Manager deploy remaining services via Flux kubectl get helmreleases -A ``` #### Accessing the Private AKS Cluster Because the AKS cluster is private by default, you must access it from within the provisioned VNet. Two options are provided: **Option A: From the jump host VM (via Azure Bastion)** The Bastion host and jump host VM are provisioned when `bastion.create: true`. Connect via the Azure Portal (Bastion blade) or using the helper script: ```bash theme={null} python3 scripts/connect_private_aks.py --run-mode=local --bootstrap-jump-host ``` **Option B: Using `az aks command invoke`** When `aks.run_command_enabled: true`, you can run kubectl commands without VPN access: ```bash theme={null} az aks command invoke \ --resource-group \ --name sgpazaks \ --command "kubectl get helmreleases -A" ``` ### Step 4: Configure DNS After `terraform apply` completes, retrieve the Front Door endpoint hostname: ```bash theme={null} terraform output frontdoor_endpoint_host_name terraform output frontdoor_dns_zone_id ``` Configure a CNAME record in your DNS provider pointing your `deployment_url` to the Front Door endpoint (the `azurefd.net` hostname from the output above). If using Azure DNS (the DNS zone is managed by Terraform when `frontdoor.create: true`), CNAME records are created automatically. Verify with: ```bash theme={null} az network dns record-set list \ --resource-group \ --zone-name ``` ### Step 5: Verify the Deployment Wait for all services to be ready: ```bash theme={null} kubectl get helmreleases -A # All should show Ready=True kubectl get pods -A # All should be Running or Completed ``` System Manager continuously reconciles the desired state. The `bootstrapping.desiredState` value in your environment YAML is written to a secret in Azure Key Vault during `terraform apply`, and System Manager reads from that secret at runtime. If a HelmRelease shows `Ready=False`, check its events: ```bash theme={null} kubectl describe helmrelease -n ``` ### Step 6: Configure Identity Provider #### SAML Configuration Set `authType: "SAML"` in your environment YAML, then configure your Identity Provider with: * **Service Entity ID**: `https://auth.` * **Redirect URI**: `https://auth./dashboard/org/saml/callback` Update the `is-saml-secret` secret in Key Vault (or via System Manager GUI): ```json theme={null} { "id": "", "samlConfiguration": { "entityId": "", "x509Cert": "", "ssoUrl": "", "attributeMappings": { "email": "", "firstName": "", "lastName": "" } } } ``` #### OIDC Configuration Set `authType: "OIDC"` in your environment YAML, then configure your Identity Provider with: * **Redirect URI**: `https://auth./dashboard/org/oidc/callback` Update the `is-oidc-secret` secret in Key Vault (or via System Manager GUI): ```json theme={null} { "id": "", "oidcConfiguration": { "clientId": "", "clientSecret": "", "issuer": "", "authorizationUrl": "", "tokenUrl": "", "userInfoUrl": "" } } ``` After modifying an identity secret, restart System Manager to apply the changes: ```bash theme={null} kubectl rollout restart deployment sgp-system-manager -n sgp-system-manager ``` ## Accessing the Platform If all goes smoothly, you should be able to navigate to the SGP platform at `https://.workspace.egp.scale.com` (or your custom domain) and authenticate via the configured identity provider. # GCP Architecture Reference Source: https://docs.gp.scale.com/docs/infrastructure/gcp/gcp-architecture-reference Overview of the SGP GCP architecture ## Architecture Diagram ```mermaid theme={null} graph TD USER(["Client"]) ENGINEER(["Engineer"]) subgraph GCP["GCP Project"] GLB["Global Load Balancer
Static IP · TLS 1.2+"] IAP["Cloud IAP"] subgraph VPC["VPC — 10.0.0.0/16"] INGRESS["Istio Ingress"] subgraph GKE["GKE Cluster"] SYS["System Pool
3× n2-standard-4"] CPU["CPU Pool
4–30× n2-standard-16"] end BASTION["Bastion VM
e2-micro"] end subgraph PEERED["VPC-Peered — Cloud SQL"] PG_MAIN[("Cloud SQL
PostgreSQL 17 (main)")] PG_TEMP[("Cloud SQL
PostgreSQL 17 (temporal)")] end SM["Secret Manager"] subgraph GCS_GROUP["Cloud Storage"] BUCKET[("GCS — platform")] KB[("GCS — knowledge base")] end AR["Artifact Registry
Docker + Helm"] KMS["Cloud KMS
CMEK (optional)"] end USER --> GLB GLB --> INGRESS INGRESS --> CPU CPU -->|"VPC Peering · SSL enforced"| PG_MAIN CPU -->|"VPC Peering · SSL enforced"| PG_TEMP CPU -->|"Workload Identity"| SM CPU -->|"Workload Identity"| BUCKET CPU -->|"Workload Identity"| KB AR -->|"Workload Identity"| CPU KMS -.->|"encrypts"| PG_MAIN KMS -.->|"encrypts"| PG_TEMP KMS -.->|"encrypts"| BUCKET ENGINEER --> IAP --> BASTION -->|"private API"| GKE ``` *** ## Terraform Structure SGP's GCP infrastructure is provisioned in two separate Terraform phases with different privilege levels: ```mermaid theme={null} graph LR CREDS(["Personal GCP
Credentials"]) subgraph PS["projectsetup/ — run once with personal credentials"] SA["Terraform
Service Account"] AR_PS["Artifact Registries
Docker + Helm"] SMK["SA Key stored in
Secret Manager"] end subgraph DEP["deployments/name/ — SA credentials auto-loaded from Secret Manager"] GKE_D["GKE Cluster"] SQL_D["Cloud SQL × 2"] VPC_D["VPC + Subnets"] SEC_D["Secrets + IAM
+ Workload Identity"] end CREDS -->|"terraform apply"| PS SMK -->|"credentials
auto-read at plan time"| DEP ``` | Phase | Directory | Credentials | Scope | | ----------------------- | --------------------- | ---------------------------------------------------- | ---------------------------------------------------- | | Privileged bootstrap | `projectsetup/` | Your personal GCP identity | Service account, Artifact Registries, API enablement | | Deprivileged main infra | `deployments//` | Terraform service account (read from Secret Manager) | GKE, Cloud SQL, networking, IAM, secrets | The main infrastructure Terraform reads the service account key directly from Secret Manager — no key file needs to exist on disk during the infrastructure run. *** ## Resources by Type ### Compute Resources | Resource | Count | Purpose | | ---------------------------- | ----- | ------------------------------------------ | | GKE Cluster | 1 | Kubernetes orchestration | | System Node Pool (`default`) | 1 | System pods — tainted `CriticalAddonsOnly` | | CPU Node Pool (`cpu`) | 1 | Application workloads | | GPU Node Pool (`gpu`) | 0–1 | AI/ML workloads (optional) | | Cassandra Node Pool | 0–1 | Temporal database (optional) | | Bastion Host | 0–1 | Private cluster access via IAP (optional) | **Default node pool sizing:** | Pool | Machine Type | Min Nodes | Max Nodes | | ------------------ | ---------------- | --------- | ----------------------- | | System (`default`) | `n2-standard-4` | 3 | 10 | | CPU (`cpu`) | `n2-standard-16` | 4 | 30 | | GPU (`gpu`) | `a2-highgpu-1g` | 0 | 0 (disabled by default) | | Cassandra | `n2-standard-4` | 3 | 6 | All pools use `COS_CONTAINERD` image type and are preemptible by default (configurable via `node_pool_config.preemptible`). *** ### Network Resources | Resource | Count | Purpose | | ------------------- | ----- | ----------------------------------- | | VPC Network | 1 | Network boundary | | Subnetwork | 1 | Kubernetes nodes | | Secondary IP Ranges | 2 | GKE pod and service CIDRs | | Cloud DNS Zone | 1 | Internal + external name resolution | | Global Static IP | 1 | Load balancer ingress endpoint | | SSL Policy | 1 | Minimum TLS 1.2 enforcement | | Firewall Rules | 4–5 | Traffic control (offline mode) | | VPC Peering | 1 | Private connectivity to Cloud SQL | | Private Route | 0–1 | Google APIs access in offline mode | *** ### Data & Storage Resources | Resource | Count | Purpose | | ----------------------------- | ----- | ----------------------------- | | Cloud SQL (PostgreSQL 17) | 1 | Main platform database | | Cloud SQL (PostgreSQL 17) | 1 | Temporal workflow database | | GCS Bucket (main) | 1 | Platform object storage | | GCS Bucket (knowledge base) | 1 | KB document storage | | GCS Bucket (monitoring) | 0–1 | Observability data (optional) | | Vertex AI Vector Search Index | 0–N | Vector embeddings (optional) | | Cloud Firestore | 0–1 | Agentex state (optional) | Both Cloud SQL instances are private-only (no public IP) and connected to the VPC via VPC peering. SSL is enforced for all database connections (`ENCRYPTED_ONLY`). *** ### Security Resources | Resource | Count | Purpose | | -------------------------------------------------------- | ----- | ------------------------------------------- | | Secret Manager Secrets | 10+ | Platform configuration and credentials | | Cloud KMS Key Ring + Key | 0–1 | Customer Managed Encryption Keys (optional) | | Service Account (`sgp--sa`) | 1 | Main SGP workload identity SA | | Service Account (`sgp--node-pool-creator`) | 1 | Node pool creation SA | | Service Account (`sgp-tf-lp-`) | 1 | Terraform execution SA (projectsetup) | | Workload Identity Pool | 1 | GKE pod → GCP SA binding | | IAP Tunnel (Bastion) | 0–1 | Private cluster access | *** ### Artifact Resources (projectsetup phase) | Resource | Count | Purpose | | -------------------------- | ----- | -------------------- | | Artifact Registry (Docker) | 1 | SGP container images | | Artifact Registry (Helm) | 1 | SGP Helm charts | Both registries are named `sgp--docker-repository` and `sgp--helm-repository`. *** ### Monitoring Resources | Resource | Count | Purpose | | -------------------------- | ----- | -------------------------------------------- | | Cloud Logging | 1 | GKE system components, API server, workloads | | VPC Flow Logs | 0–1 | Network traffic sampling (optional) | | GKE Vulnerability Scanning | 1 | Basic vulnerability mode enabled by default | *** ## Network Architecture ### Address Space ``` VPC: sgp--network └── Subnet: sgp--network-kubernetes-subnet ├── Primary range: 10.0.0.0/16 (65,536 IPs — GKE nodes) ├── Secondary range: 10.2.0.0/16 (65,536 IPs — Kubernetes services) └── Secondary range: 10.4.0.0/16 (65,536 IPs — Kubernetes pods) Cloud SQL VPC Peering: └── Reserved range: /16 block (managed by service networking) GKE Control Plane (private): └── Master CIDR: 10.5.0.0/28 (configurable via private_gke_master_ipv4_cidr_block) ``` ### Traffic Flow **Ingress (External):** ``` Internet → Global Load Balancer (static IP) → Istio Ingress → Pod (TLS terminated at load balancer; SSL policy enforces TLS 1.2+) ``` **Database Access:** ``` GKE Pod → VPC Peering → Cloud SQL (private IP) (SSL enforced; no public IP on Cloud SQL instances) ``` **Google APIs (offline mode):** ``` GKE Pod → Private Google Access → Google APIs (199.36.153.4/30) (Dedicated route; no default internet gateway route created) ``` **Bastion Access:** ``` Engineer → IAP Tunnel → Bastion VM (e2-micro) → GKE API (private endpoint) ``` *** ## GKE Configuration ### Cluster Features | Feature | Value | | -------------------------- | -------------------------------------------- | | Datapath provider | `ADVANCED_DATAPATH` (eBPF-based) | | IP stack | Dual-stack IPv4/IPv6 | | Workload Identity | Enabled (`.svc.id.goog`) | | Secret Manager integration | Enabled | | Vulnerability scanning | Basic mode | | Private nodes | Enabled (when `offline_mode = true`) | | Private endpoint | Configurable (`enable_gke_private_endpoint`) | | Master authorized networks | Configurable per deployment | | DNS endpoint | Enabled (allows external cluster DNS access) | ### Workload Identity GKE pods authenticate to GCP services using Workload Identity rather than node-level service account keys. Kubernetes service accounts are bound to GCP service accounts via the workload identity pool: ``` .svc.id.goog[/] → GCP Service Account → Secret Manager, GCS, Artifact Registry, etc. ``` Key bindings provisioned by Terraform: | Kubernetes Identity | GCP Role | | ----------------------------------------------- | --------------------------------------------------------------- | | `sgp-system-manager` (system-manager namespace) | `secretmanager.viewer`, `secretmanager.secretAccessor` | | `sgp-system-manager-pre-install` | `secretmanager.viewer`, `secretmanager.secretAccessor` | | `egp-api-backend` (egp namespace) | `secretmanager.secretAccessor`, GCS access via SA impersonation | | `egp-api-backend-db-setup` | `secretmanager.secretAccessor` | | `agents` (agents namespace) | `secretmanager.secretAccessor` | *** ## Secret Manager Secrets Key secrets provisioned by Terraform: | Secret Name | Contents | | --------------------------------------- | ------------------------------------------------------------------------------ | | `-system-manager-config` | System Manager runtime configuration (URLs, workspace ID, cloud provider info) | | `-system-manager-desired-state` | Initial desired state (pack list) — managed externally after first apply | | `-saml-config-secret` | SAML IdP configuration | | `-oidc-config-secret` | OIDC IdP configuration | | `terraform-service-account-key-secret` | Terraform SA private key (used by `deployments/` as provider credentials) | The `secret_name_prefix` variable (typically `sgp-`) scopes secrets for deployments in shared GCP projects. *** ## Customer Managed Encryption Keys (CMEK) When `useCustomerManagedEncryptionKey = true`, Terraform provisions a Cloud KMS key ring and symmetric encryption key: | Resource | Name | | ---------- | ------------------------------- | | Key Ring | `sgp--key-ring` | | Crypto Key | `sgp--crypto-key` | The key is applied to: * Cloud SQL instances (both main and Temporal) * GCS buckets CMEK is recommended for production deployments to maintain cryptographic control over data at rest. *** ## Optional Capabilities Each optional capability provisions dedicated infrastructure: | Capability | Variable | Additional Resources | | ---------------------------- | ---------------------------------- | --------------------------------------------------------------------------- | | Agentex | `deployAgentex` | Firestore, dedicated service account, GCS bucket | | Workflows | `deployCompass` | Cloud SQL database, GCS bucket, service account | | Dex (Document Understanding) | `enable_dex` | Cloud SQL (`db-custom-4-15360`), GCS bucket, service account | | Reducto | `enable_reducto` | Cloud SQL (`db-custom-2-7680`), GCS bucket, Vision API key, service account | | Model Engine | `deployModelEngine` | Vertex AI, additional node pools | | Monitoring | `enable_monitoring` | GCS bucket, IAM bindings | | Cloud Build | `enable_cloud_build` | GCS bucket, Artifact Registry, service accounts | | Vertex AI Search | `vertex_ai_indices` | Vector Search indices and endpoints | | LiveKit | `provision_livekit_infrastructure` | GCS bucket (audio), IAM bindings | # GCP SGP Deployment Source: https://docs.gp.scale.com/docs/infrastructure/gcp/gcp-sgp-deployment End-to-end guide to deploying SGP in a GCP project. ## Overview This guide walks you through deploying SGP in a GCP project using the SGP GCP Terraform modules. SGP GCP infrastructure is defined by terraform modules managed by Scale. GCP deployment is split into two Terraform phases with different privilege levels: * **`projectsetup/`** — Run once with privileged GCP credentials. Creates a dedicated Terraform service account (with least privileges), enables required GCP APIs, and provisions Artifact Registries for SGP images and Helm charts. * **`deployments//`** — Run with the service account created above. Provisions the GKE cluster, Cloud SQL, networking, and all SGP infrastructure. This split ensures the main infrastructure Terraform never requires broad privileged credentials. ## Prerequisites * Access to a GCP project with permissions to manage IAM, APIs, and compute resources * The following tools installed: * [gcloud CLI](https://cloud.google.com/sdk/docs/install) (`gcloud auth login` completed) * [Terraform](https://www.terraform.io/) (`>= 1.1.7`) * [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) * [helm](https://helm.sh/docs/intro/install/) (for cluster bootstrap) * [crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane) or [Docker](https://docs.docker.com/engine/install/) (for copying images) * \[sgpctl] ([https://github.com/scaleapi/sgpctl](https://github.com/scaleapi/sgpctl)) (cli tool to execute system manager related commands) * The following from Scale: * The SGP GCP Infrastructure modules (`gcp/`) * A `workspace_id` (8-digit number) and `registration_secret` unique to your deployment * A new application configured in your identity provider to authenticate to the SGP platform (SAML or OIDC) (optional) * A custom domain for your deployment (optional) ## Installation ### Step 1: Bootstrap the Project Navigate to the `projectsetup/` directory. This phase creates the Terraform service account and Artifact Registries. Edit the `locals` block at the top of `projectsetup/main.tf`: ```hcl theme={null} locals { project = "" region = "" # e.g. "us-east1" zone = "" # e.g. "us-east1-a" service_account_name = "-sa" # Suffix for the service account name workspaceID = "" # 8-digit number from Scale, e.g. "90000001" } ``` Then initialize and apply: ```bash theme={null} cd projectsetup terraform init terraform apply ``` This creates: * A Terraform service account (`sgp-tf-lp-`) with the IAM roles needed to provision SGP infrastructure * A service account key stored as `terraform-service-account-key-secret` in Secret Manager * Docker and Helm Artifact Registries (`sgp--docker-repository`, `sgp--helm-repository`) * All required GCP APIs enabled on the project The Terraform outputs include the `service_account_email` of the newly created service account, which is useful for auditing. The service account key is automatically stored in Secret Manager and read by the main infrastructure Terraform — no manual key management is required. ### Step 2: Copy SGP Images and Helm Charts *This step is only required if your deployment is configured to block internet access from the cluster (`offline_mode` in step 3).* Before provisioning the main infrastructure, copy SGP's Docker images and Helm charts from Scale's registry into your GCP Artifact Registry. **Download the manifest from Scale's control plane**: ```bash theme={null} sgpctl manifest download \ --control-plane-id \ --registration-token \ --version \ --channel \ --output .manifest.yaml ``` Note: sgpctl manifest download requires internet access to [https://control.egp.scale.com](https://control.egp.scale.com), so it must be run from a connected machine. Users with air-gapped operator workstations should run it from their laptop and SCP the output to wherever they'll run the mirror commands. **Authenticate to source + destination registries**: ```bash theme={null} # Source: Scale's ECR (credentials from Scale engineer) crane auth login 022465994601.dkr.ecr.us-west-2.amazonaws.com \ --username AWS \ --password-stdin # Destination: customer's GCP Artifact Registry (uses gcloud creds) gcloud auth configure-docker -docker.pkg.dev ``` **Mirror images and charts**: ```bash theme={null} # Container images sgpctl mirror-manifest \ --manifest-file .manifest.yaml \ --dest-docker-registry -docker.pkg.dev//sgp--docker-repository # Helm charts sgpctl mirror-helm-charts \ --manifest-file .manifest.yaml \ --dest-helm-repo oci://-docker.pkg.dev//sgp--helm-repository ``` **Verify the mirror**: ```bash theme={null} gcloud artifacts docker images list \ -docker.pkg.dev//sgp--docker-repository \ --include-tags --limit=20 gcloud artifacts docker images list \ -docker.pkg.dev//sgp--helm-repository \ --include-tags --limit=20 ``` ### Step 3: Configure the Deployment Copy the `deployments/prototype/` directory and rename it for your deployment: ```bash theme={null} cp -r deployments/prototype deployments/ cd deployments/ ``` Edit the `locals` block at the top of `main.tf`. The deployment reads its Terraform service account credentials automatically from Secret Manager — no key file is required on disk. ```hcl theme={null} locals { project = "" region = "" # e.g. "us-east1" zone = "" # e.g. "us-east1-b" # ── Deployment identity ──────────────────────────────────────────────── workspaceID = "" # 8-digit number from Scale registrationSecret = "" # From Scale deploymentURL = ".workspace.egp.scale.com" # Or your custom domain # ── Repositories ────────────────────────────────────────────────────── # baseRepository: used by workloads running inside the cluster to pull images # publicBaseRepository: used for initial image pulls during bootstrap (before private DNS resolves) # Both point to the same registry when using a private Artifact Registry. baseRepository = "-docker.pkg.dev//sgp--docker-repository" publicBaseRepository = "-docker.pkg.dev//sgp--docker-repository" # ── System Manager ──────────────────────────────────────────────────── systemManagerImageTag = "" # From Scale; must match manifest.yaml offline_mode = true # true when using your own Artifact Registry # ── Auth ────────────────────────────────────────────────────────────── authType = "SAML" # "SAML" or "OIDC" # ── Bootstrap ───────────────────────────────────────────────────────── # deploy_system_manager=false: Terraform creates infra only; run manual-helm-install.sh to bootstrap # deploy_system_manager=true: Terraform also installs System Manager into the cluster deploy_system_manager = false # ── DNS ─────────────────────────────────────────────────────────────── createDNSRecords = false # Set true if Terraform should manage Cloud DNS records # ── Encryption ──────────────────────────────────────────────────────── useCustomerManagedEncryptionKey = false # Set true for CMEK (recommended for production) # ── Networking ──────────────────────────────────────────────────────── gke_config = { private_endpoint = true master_authorized_networks = [ { cidr_block = "10.0.0.0/16", display_name = "cluster vpc primary range" }, { cidr_block = "10.2.0.0/16", display_name = "cluster vpc services range" }, { cidr_block = "10.4.0.0/16", display_name = "cluster vpc pod range" }, ] } } ``` **If using SAML,** place your IdP's x509 certificate (without BEGIN/END lines) as `x509.cer` in the deployment directory. Update the `samlConfigSecret` in the `module "sgp"` block: ```hcl theme={null} module "sgp" { source = "../../modules/sgp/" samlConfigSecret = jsonencode({ "id" = local.workspaceID "samlConfiguration" = { "entityId" = "https://auth.${local.deploymentURL}" "x509Cert" = file("${path.module}/x509.cer") "ssoUrl" = "" "attributeMappings" = { "email" = "" "firstName" = "" "lastName" = "" } } }) # ... other variables } ``` **If using OIDC,** update `oidcConfigSecret` instead: ```hcl theme={null} oidcConfigSecret = jsonencode({ "id" = local.workspaceID "clientId" = "" "clientSecret" = "" "issuer" = "" "authorizationUrl" = "" "tokenUrl" = "" "userInfoUrl" = "" }) ``` ### Step 4: Provision Infrastructure via Terraform From the deployment directory, initialize and apply: ```bash theme={null} terraform init -upgrade terraform plan -out=tfplan # Inspect the plan before proceeding terraform show tfplan terraform apply tfplan ``` *This step can take 30–60 minutes due to GKE cluster provisioning and Cloud SQL setup.* After apply completes, connect to the cluster: ```bash theme={null} gcloud container clusters get-credentials sgp--kubernetes-cluster \ --region \ --project ``` If `gke_config.private_endpoint = true`, the cluster API server is only accessible from within the VPC. Use the provisioned bastion host (via IAP) or a network-connected runner to access it. Set `bastion_enabled = true` and add your user email to `bastion_iap_members` in the `security_compliance` block to enable bastion access. ### Step 5: Bootstrap the Cluster [SGP System Manager](/docs/infrastructure/system-manager) orchestrates SGP service deployment. If you set `deploy_system_manager = false` in the previous step, bootstrap it manually using the provided script. Edit `manual-helm-install.sh` in your deployment directory with the correct values: ```bash theme={null} PROJECT_ID="" PROJECT_NUMBER="" # From: gcloud projects describe --format='value(projectNumber)' BASE_REPOSITORY="-docker.pkg.dev//sgp--docker-repository" SYSTEM_MANAGER_IMAGE_TAG="" OFFLINE_MODE=true # Obtain from Cloud SQL instance → "Connect to this instance" in the console, or from Terraform state SPICEDB_IP_ADDRESS="" SPICEDB_PASSWORD="" TEMPORAL_DB_PASSWORD="" ``` Then run the bootstrap script: ```bash theme={null} chmod +x manual-helm-install.sh ./manual-helm-install.sh ``` This installs System Manager into the cluster. System Manager will then begin reconciling the `desired_state.json` file and deploying SGP services automatically. #### Desired State The `desired_state.json` file in your deployment directory defines which SGP packs System Manager installs. Update it to reference your GCP Artifact Registry: ```json theme={null} { "version": "0.1", "packs": [ { "name": "flux" }, { "name": "sgp-helm-repository", "properties": { "helm-repo": { "url": "oci://-docker.pkg.dev//sgp--helm-repository" } } }, { "name": "istio" }, { "name": "sgp-base", "properties": { "helm-base": { "value_overrides": { "refreshRegcred": { "enabled": false }, "gcp": { "presharedCertificates": ["sgp--ssl-certificate"] } } } } }, { "name": "spicedb" }, { "name": "identity-service" }, { "name": "temporalf" }, { "name": "sgp-apps" } ] } ``` See [Step 6](#step-6-configure-ssl-certificates) for the SSL certificate options and how to set the correct value in the `gcp` block above. ### Step 6: Configure SSL Certificates Three options are available, in order of preference: #### Option 1: Google Certificate Manager (Recommended) Provisions a wildcard certificate (`*.your-domain.com`) via DNS authorization. Covers all subdomains automatically. Enable it in `main.tf`: ```hcl theme={null} module "sgp" { source = "../../modules/sgp/" enable_certificate_manager = true # ... } ``` After `terraform apply`, note the two outputs: * `certificate_manager_dns_auth_record` — a CNAME you must add to your DNS zone for domain validation * `certificate_map_name` — typically `sgp--cert-map` Add the CNAME to your DNS provider, then update `desired_state.json`: ```json theme={null} { "name": "sgp-base", "properties": { "helm-base": { "value_overrides": { "gcp": { "certificateMapName": "sgp--cert-map" } } } } } ``` The certificate typically provisions within 10–60 minutes of the DNS record being in place. #### Option 2: Google Managed Certificate Google provisions and auto-renews per-subdomain certificates. Does not require uploading a certificate, but requires DNS to resolve to the load balancer IP before provisioning, and does not support wildcards. This is the default when neither `certificateMapName` nor `presharedCertificates` is set in the `sgp-base` pack's `gcp` block. Simply omit those keys from `desired_state.json`. #### Option 3: Preshared Certificate Use a certificate you manage and upload to Google Cloud. Required when DNS is not publicly resolvable or Certificate Manager is not available. Upload the certificate: ```bash theme={null} gcloud compute ssl-certificates create sgp--ssl-certificate \ --certificate=fullchain1.pem \ --private-key=privkey1.pem \ --project ``` Reference it in `desired_state.json`: ```json theme={null} { "name": "sgp-base", "properties": { "helm-base": { "value_overrides": { "gcp": { "presharedCertificates": ["sgp--ssl-certificate"] } } } } } ``` Preshared certificates must be renewed manually. Let's Encrypt certificates expire after 90 days. After changing the SSL configuration in `desired_state.json`, restart System Manager to apply it: ```bash theme={null} kubectl rollout restart deployment sgp-system-manager -n sgp-system-manager ``` ### Step 7: Configure DNS Get the external IP address of the load balancer: ```bash theme={null} # From inside the cluster: kubectl get svc istio-ingress --namespace istio-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}' # Or from gcloud (the global address is created by Terraform): gcloud compute addresses describe sgp--external-ip \ --global \ --project \ --format="value(address)" ``` Create DNS A records in your DNS provider pointing to this IP for: * `` (apex) * `api.` * `auth.` * `app.` If `createDNSRecords = true` in your `main.tf` locals, Terraform manages a Cloud DNS zone and creates these records automatically. Retrieve the name servers from the Terraform output: ```bash theme={null} terraform output name_servers ``` Then delegate the zone by configuring these as NS records at your domain registrar. ### Step 8: Verify the Deployment Wait for all services to be ready: ```bash theme={null} kubectl get helmreleases -A # All should show Ready=True kubectl get pods -A # All should be Running or Completed ``` System Manager continuously reconciles the desired state. If a HelmRelease shows `Ready=False`, check its events: ```bash theme={null} kubectl describe helmrelease -n ``` ## Accessing the Platform Once all HelmReleases are ready and DNS resolves correctly, navigate to `https://.workspace.egp.scale.com` (or your custom domain) and authenticate via your configured identity provider. # System Manager Source: https://docs.gp.scale.com/docs/infrastructure/system-manager Overview of the SGP System Manager deployment orchestrator ## Overview System Manager is a Kubernetes operator used to deploy and manage the SGP platform. It is responsible for deploying the SGP platform services and agents to an existing Kubernetes cluster. ## Installation System Manager is installed as a Helm chart into the Kubernetes cluster during the deployment of the SGP platform. See your cloud provider's corresponding deployment guide for more information. ## Configuration System Manager is configured via `system-manager-config.json`. This file is stored in the cloud provider's secret manager. To modify the configuration, you can either update the secret directly or use the System Manager GUI, then restart the System Manager deployment to apply the changes. ```bash theme={null} kubectl rollout restart deployment sgp-system-manager -n sgp-system-manager ``` An example configuration file is shown below. The `aws` block is only present for AWS deployments; GCP and Azure deployments use equivalent `gcp` and `azure` blocks instead. The `baseRepository` format also varies by cloud provider (e.g., `oci://-docker.pkg.dev//sgp--helm-repository` for GCP, `oci://.dkr.ecr..amazonaws.com/sgp--helm-repository` for AWS). ```json theme={null} { "cloudProvider": "", "baseRepository": "", "samlSetupEnabled": true, "oidcSetupEnabled": true, "deploymentURL": "https://.workspace.egp.scale.com", "workspaceId": "", "authType": "", "baseDomain": "", "scaletrain_tenant_prefix": "", "train_tenant_prefix": "", "deployAgentex": true, "deploySae": true, "aws": { "accountId": "", "region": "", "prefix": "", "modelEngineS3Bucket": "scale-egp--ml", "sqsQueuePolicyTemplate": "", "sqsQueueTagTemplate": "", "clusterName": "", "karpenterIrsaArn": "", "targetGroupArn": "", "nodeSubnets": "", "nodeSecurityGroup": "", "postgresHostTemporal": "", "compassBucketName": "", "compassMongoHost": "", "compassRedisHost": "", "reductoBucketName": "", "reductoDatabaseUrl": "", "reductoIrsaRoleArn": "", "reductoAzureVisionEndpoint": "", "reductoAzureVisionKey": "", "codeBuildProjectName": "", "codeBuildS3Bucket": "", "codeBuildRegistryUrl": "", "codeBuildServiceRoleArn": "", "cloudDeployEnabled": true, "dex": { "irsaRoleArn": "", "prefix": "duc-" }, "identities": { "sgpModels": { "irsaArn": "", "secretArns": { "backend": "", "model-providers": "" } } }, "train": { "irsaRoleArn": "", "sagemakerExecutionRoleArn": "", "sagemakerSecurityGroupId": "", "databaseHost": "", "dataBucket": "", "checkpointsBucket": "", "outputBucket": "", "stagingBucket": "" }, "registryProxy": { "irsaRoleArn": "", "ecrRepositoryUrl": "", "jwtSecret": "", "workspaceConfig": "" } }, "frontDoorSSLCertB64": "", "frontDoorSSLKeyB64": "", "initialDesiredState": "", "datadog": { "enabled": true, "env": "", "clusterName": "", "secretName": "", "irsaRoleArn": "" } } ``` ## Architecture System Manager runs as a deployment in the `sgp-system-manager` namespace. Its GUI is accessible on port 8000. ```bash theme={null} kubectl port-forward deployment/sgp-system-manager -n sgp-system-manager 8000:8000 ``` Then navigate to `http://localhost:8000` in your browser to access the GUI. ```mermaid theme={null} graph TD ADMIN(["Platform Admin"]) subgraph CLUSTER["Kubernetes Cluster"] subgraph SM_NS["sgp-system-manager namespace"] GUI["Web GUI
:8000"] API["REST API
/api/v3"] WATCHER["Secret Watcher
polls every 3s"] INSTALLER["Pack Installer"] end subgraph RESOURCES["Cluster Resources"] NS["Namespaces"] SEC["Secrets + Pull Secrets"] HR["HelmRelease CRDs"] end subgraph FLUX_NS["flux-system namespace"] FLUX["Flux Helm Controller"] end PODS["SGP Services
(Helm-managed pods)"] end subgraph CLOUD["Cloud Provider"] SM_SECRET["Secret Manager
desired-state · config"] HELM_REPO["Helm Repository
OCI Artifact Registry"] end ADMIN -->|"browser"| GUI GUI --> API API -->|"read / write"| SM_SECRET WATCHER -->|"detect external changes"| SM_SECRET API --> INSTALLER INSTALLER --> NS INSTALLER --> SEC INSTALLER --> HR FLUX -->|"watches"| HR FLUX -->|"pull chart"| HELM_REPO FLUX -->|"deploy"| PODS ``` ### Packs System Manager organizes services into "packs". Each pack is a collection of resources that are deployed together. Generally packs are composed of [FluxCD HelmRelease](https://fluxcd.io/flux/components/helm/helmreleases/) as well as other resources necessary to support a particular service. When a pack is installed, System Manager renders its resource templates and writes the resulting Kubernetes resources — namespaces, secrets, and FluxCD HelmRelease CRDs — to the cluster. FluxCD then picks up the HelmRelease CRDs and handles pulling and deploying the Helm charts. ### FluxCD System Manager offloads resource reconciliation to FluxCD. FluxCD is a tool that allows you to manage the lifecycle of your Kubernetes resources. It is responsible for ensuring that the desired state of the resources is maintained. For more information on FluxCD, see the [FluxCD documentation](https://fluxcd.io/flux/components/helm/helmreleases/). ### desired-state.json The collection of packs that System Manager will deploy is defined in the `desired-state.json` file. This file is stored in the cloud provider's secret manager. To modify the desired state, you can either update the secret directly or use the System Manager GUI, then trigger reconciliation via the System Manager GUI. A sample desired state file is shown below: ```json theme={null} { "version": "0.1", "packs": [ { "name": "flux" }, { "name": "sgp-helm-repository" }, { "name": "istio" }, { "name": "spicedb" }, { "name": "identity-service" }, { "name": "temporalf" }, { "name": "sgp-apps" } ] } ``` # Creating an Agent Source: https://docs.gp.scale.com/docs/v5/agents/agent-service/lifecycle/creating-agent-service-agents How to create and publish an agent in the SGP UI. #### Recommendation & Future Development While Agent Service remains supported, **Agentex is the recommended path** for all new projects. Future development, new features, and enhancements will be focused primarily on Agentex. ## 1. Give your agent a name The first step is to give your agent a name. This will be used to identify the agent in the SGP UI. 1. Navigate to the [agents page](https://egp.dashboard.scale.com/beta/build). 2. Create a new Agent Service Agent using the top right **Create Agent** button. 3. Fill in the name of the agent and an optional description and click **Create Agent**. Create Agent Service Agent A draft version of the agent will be created and you will be redirected to the agent's CI/CD page. ## 2. Configure your agent Now you should see your agent's CI/CD page with a YAML editor featured prominently. Any updates you make to the YAML will be automatically saved. Blank YAML editor This YAML configuration is what is used to build the agent on the fly using Agent Service. You can copy and paste or drag and drop an existing YAML file from your local device. Alternatively, you can get started by selecting a template from the **Use a template** menu in the bottom. This is a library of preconfigured YAML files or snippets that we are constantly expanding. Selecting the template will immediately update the YAML in the editor, which you can modify. For example, you could swap the `llm_model` from `gpt-4o-mini` to another one like `llama-3-1-70b-instruct` for an open source alternative. Note that these models are directly interfacing with SGPs model API and can be called by name or by using the `model_deployment_id`. Template Agent Often, it is useful to visualize this workflow. To do that, you can swap to the **Graph** view on the top left, which will show a node visualization of your workflow or state machine application. Clicking on any of the nodes shows its configuration. Graph View ## 3. Test your agent You can easily test the current configuration (as long as it is a valid YAML). To do that click **Test** in the bottom menu. Test Agent By changing your YAML configuration and continuously testing its output, you can iteratively adjust your agent until it has reached the desired state. The **Threads** panel on the right supports multi-turn by default. If you want to reset context and start a new thread, just click **New Thread**. The YAML configuration is auto-saved continuously to ensure progress is lost. ## 4. Publish your agent Once you want to go ahead and publish the configuration for deployment, you can hit **Publish Agent** in the top right. After an agent is published its configuration is immutable and cannot be changed. Only its name and description can be updated. Published Agent ## What's next? Using the templates captures only a tiny fraction of what is possible with Agent Service. To write you own custom configurations you need to learn the set of available nodes, connections and techniques. Learn more in our v4 documentation on [Building Agents](/docs/agents/hello-world). # Deploying an Agent Source: https://docs.gp.scale.com/docs/v5/agents/agent-service/lifecycle/deploying-agent-service-agents How to deploy and interact with agents in the SGP UI. #### Recommendation & Future Development While Agent Service remains supported, **Agentex is the recommended path** for all new projects. Future development, new features, and enhancements will be focused primarily on Agentex. ## Introduction Agents that are configured with YAML in SGP and are fully executed on the platform's backend. The application logic defined in your YAML configuration determines how the agent processes inputs and produces outputs. ## Live Execution from YAML Editor As you configure your agent in YAML, you can execute it within SGP's interface to see how it behaves. The execution output is shown in the right-hand panel, on a per-node basis. This allows you to debug and validate your agent's flow in real time: Test Agent Outputs can be viewed either as raw JSON or in a more readable, formatted display. Toggle between these modes to best understand or debug your agent's responses. ## Providing User Inputs In the Agent Service CI/CD page, you will be prompted to provide inputs that are designated as `user_question` in the YAML config. ```yaml theme={null} workflow: - name: query_llm type: generation config: llm_model: gpt-4o-mini temperature: 0.2 inputs: input_prompt: user_question ``` These inputs will be rendered in the inputs panel. You can provide them as either raw JSON or fill them in via form fields. If you do not specify user inputs or type them, you will have to use JSON inputs. Hitting submit starts an execution of the agent as a run. Submit Input ## Deploying an Agent From the Agent Service CI/CD page, click **Deploy Agent**. Then enter a custom slug (a unique identifier) for your url. By default this is set to the agent's id. Click **Deploy**, which creates a simple web-based chat interface you can access. Deploy Agent When an agent is deployed, you will see a success message and a link to the deployed agent in the header. Deployed Agent ## Interacting with a Deployed Agent Once deployed, you can visit the web app at the slug you specified. You can chat with the agent in a more intuitive, conversational format. The chat interface supports threads, user feedback, and dynamic inputs (if the agent requires more than one input per turn). Chat Interface ## Customizing a Deployed Agent Once an agent is deployed, you can customize it by clicking the **Manage Deployment** tab. From there you can: * Change the slug (URL) of the deployed agent * Change the theme of the deployed agent * Change the evaluation settings of the deployed agent ### Changing the slug To change the slug of the deployed agent, simply enter a new slug in the input field and click **Redeploy**. Redeploy Agent ### Changing the theme To change the theme of the deployed agent, simply select a new theme from the dropdown. This will apply the theme to the deployed agent without having to redeploy. You can also create a new theme by clicking **Create New Theme** in the dropdown. This will open the theme editor, where you can customize the theme to your liking. Create Theme ### Changing the evaluation settings You can enable or disable continuous evaluation for your agent by toggling the **Online Evaluation** switch. Online Evaluation # Monitoring an Agent Source: https://docs.gp.scale.com/docs/v5/agents/agent-service/lifecycle/monitoring-agent-service-agents How to monitor and evaluate agents in the SGP UI. #### Recommendation & Future Development While Agent Service remains supported, **Agentex is the recommended path** for all new projects. Future development, new features, and enhancements will be focused primarily on Agentex. ## Monitoring Executions Every time an agent is run (during testing or otherwise), it is recorded and can be viewed on the traces page. This provides a comprehensive view of all threads and their inputs/outputs, helping you track performance and diagnose issues. You can navigate to the traces page by clicking the **View Traces** button in the agent header. View Traces From the traces page you can: * View the inputs and outputs of each thread * View the performance metrics of each thread * View the metadata of each thread * Filter and search for specific threads Traces Page Clicking on a trace will open the trace details in a modal. Individual Trace # Introduction to Agent Service Source: https://docs.gp.scale.com/docs/v5/agents/agent-service/overview An overview of SGP's Agent Building Framework #### Recommendation & Future Development While Agent Service remains supported, **Agentex is the recommended path** for all new projects. Future development, new features, and enhancements will be focused primarily on Agentex. Welcome to the introductory guide for **Agent Service**. This document is designed for engineers who are new to Agent Service and want to learn how to build GenAI agents using our framework. Agent Service provides the backend for SGP applications, enabling the creation, deployment, and execution of powerful AI solutions based on Large Language Models (LLMs) and other AI models. To get a more in depth overview of Agent Service, check out the [v4 docs](/docs/agents/agent-service) page. ## What is Agent Service? Agent Service is a software framework that provides a set of modular building blocks that make it easy to create, deploy, and run AI applications. These applications can perform a variety of tasks, including: * **Document Analysis** * **Chatbot Responses** * **Custom AI Solutions** While Agent Service is primarily designed to support LLM-based applications, it can also accommodate other types of models (for example, multi-modal LLMs) on an ad-hoc basis. ## Key Features * **Modular Design:** Build applications using pre-configured components that can be combined like Lego blocks. * **Flexible Architecture:** Support both simple, linear workflows and complex, interactive applications with ease. * **Seamless Integration with SGP:** Easily integrate with existing SGP tools and APIs for functionalities such as information retrieval and text generation. ## Core Components Agent Service applications are constructed using several key components: ### Node * **Definition:** A node represents a single operation such as generation, retrieval, or data transformation. * **Examples:** * **Generation Nodes:** Handle LLM-based text generation and chat responses. * **Reranker & Retriever Nodes:** Enhance search capabilities and ensure relevance when working with large datasets. * **Citation & Code Execution Nodes:** Provide operations like reference generation or executing code for data transformation. ### Workflow * **Definition:** A workflow is a sequence of nodes arranged in a specific order. It aggregates data from user inputs or previous outputs. * **Features:** * Supports parallel processing for maximum execution speed. * Offers flexibility in how nodes are combined and executed. ### State Machine * **Definition:** A state machine is a structured flow that transitions between different states (or workflows) based on outcomes. * **Capabilities:** * Supports branching and conditional paths. * Incorporates additional user inputs for highly interactive applications. schematic overview of agent service # Agentex CI/CD Source: https://docs.gp.scale.com/docs/v5/agents/agentex/agentex-monitoring Understand how to set up CI/CD for Agentex Agents ## Navigating to the Agentex CI/CD page 1. To view all Agentex agents in SGP navigate to the [agents page](https://egp.dashboard.scale.com/beta/build). 2. Click on an Agentex agent to navigate to the Agentex CI/CD page. Agentex CI/CD page ## Agentex CI/CD functionality From the Agentex CI/CD page you can: * View the active version of the Agentex agent * Open the agent chat interface * Navigate to the agent's traces * See the git commits corresponding to each agent deployment ## Agentex versioning The active version of the Agentex agent is displayed at the top of the page. Agentex agents are automatically deployed when a change is merged into Github. To create a new version of the Agentex agent, just push a new commit to Github and merge it into the main branch. After it's merged you'll be able to see the new version deployed in SGP, as well as the commit hashes and the timestamp of the deployment. # API and SDK Source: https://docs.gp.scale.com/docs/v5/agents/agentex/cloud-build-and-deploy/api Programmatic access to Agentex cloud builds and deployments via the REST API and Python/TypeScript SDKs. ## Overview The Build and Deploy API and SDKs give you programmatic control over Agentex cloud builds and deployments. You can create builds, poll for status, stream logs, create deployments, and tear them down through REST endpoints under `/v5/builds` and `/v5/agentex/deployments`, or through the equivalent Python and TypeScript SDK methods. The API and SDKs expose the same underlying resources as the UI, CLI, and CI/CD interfaces, so anything you create or modify programmatically is visible everywhere else. Use them when you need to integrate builds and deployments into custom automation or tooling. For full SDK documentation, see the [Python SDK reference](https://sgp-dev.stldocs.app/api/python) and [TypeScript SDK reference](https://sgp-dev.stldocs.app/api/typescript). ## Authentication Every request to the Build and Deploy API requires two headers: | Header | Description | | ----------------------- | ------------------------------------------------------------------------- | | `x-api-key` | Your SGP API key. Found in the IAM or admin section of the SGP dashboard. | | `x-selected-account-id` | Your SGP account ID. Found in the admin section of the SGP dashboard. | ```bash curl theme={null} curl -s $SGP_CLIENT_BASE_URL/v5/builds \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ```python Python theme={null} from scale_gp_beta import SGPClient client = SGPClient( api_key="your-api-key", account_id="your-account-id", ) ``` ```typescript TypeScript theme={null} import SGPClient from 'scale-gp'; const client = new SGPClient({ apiKey: process.env.SGP_API_KEY, accountId: process.env.SGP_ACCOUNT_ID, }); ``` The Python and TypeScript SDKs handle authentication headers automatically once you initialize the client. The examples in the rest of this page assume you have already created a `client` instance as shown above. ## Build lifecycle A build packages your agent source code into a container image. The lifecycle is: **create** the build, **poll** its status until it reaches a terminal state, and optionally **stream logs** or **cancel** it. See the [build status lifecycle](/docs/v5/agents/agentex/cloud-build-and-deploy/overview#build) for the full state diagram. ### Create a build To create a build, send a multipart form request with your build context archive (a `.tar.gz` of your agent source code), the target image name and tag, and the agent name. ```bash curl theme={null} curl -X POST $SGP_CLIENT_BASE_URL/v5/builds \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ -F "context_archive=@build-context.tar.gz" \ -F "image_name=my-agent" \ -F "image_tag=v1.2.0" \ -F "agent_name=my-agent" ``` ```python Python theme={null} build = client.build.create( context_archive="./build-context.tar.gz", image_name="my-agent", image_tag="v1.2.0", agent_name="my-agent", ) print(f"Build created: {build.id} (status: {build.status})") ``` ```typescript TypeScript theme={null} const build = await client.build.create({ context_archive: "./build-context.tar.gz", image_name: "my-agent", image_tag: "v1.2.0", agent_name: "my-agent", }); console.log(`Build created: ${build.id} (status: ${build.status})`); ``` The response includes the build ID and an initial status of `queued`. Use the build ID to poll for status updates and retrieve logs. Optional parameters: | Parameter | Description | | ------------ | --------------------------------------------------------- | | `build_args` | Key-value pairs passed as Docker build arguments | | `platform` | Target platform for the image build (e.g., `linux/amd64`) | ### Get build status Poll `GET /v5/builds/{build_id}` to check the current status of a build. ```bash curl theme={null} curl -s $SGP_CLIENT_BASE_URL/v5/builds/$BUILD_ID \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ```python Python theme={null} build = client.build.retrieve("build_abc123") print(f"Status: {build.status}") ``` ```typescript TypeScript theme={null} const build = await client.build.retrieve("build_abc123"); console.log(`Status: ${build.status}`); ``` ### Cancel a build You can cancel a build that is in `queued` or `running` state. ```bash theme={null} curl -X POST $SGP_CLIENT_BASE_URL/v5/builds/$BUILD_ID/cancel \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ## Deploy lifecycle A deployment takes a successfully built container image and rolls it out to a Kubernetes cluster. The lifecycle is: **create** the deployment, **poll** its status until it becomes `healthy` or `failed`, and inspect **logs** for debugging. See the [deployment status lifecycle](/docs/v5/agents/agentex/cloud-build-and-deploy/overview#deployment) for the full state diagram. ### Create a deployment To create a deployment, provide the `manifest_file` (your `manifest.yaml` content as a string), the `environment_config` (your `environments.yaml` content as a string), and a `build_id` referencing a successful build. ```bash curl theme={null} curl -X POST $SGP_CLIENT_BASE_URL/v5/agentex/deployments \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ -H "Content-Type: application/json" \ -d '{ "manifest_file": "name: my-agent\nversion: 1.0\n...", "environment_config": "environment: production\nnamespace: agents\n...", "build_id": "build_abc123" }' ``` ```python Python theme={null} deployment = client.deploy.create( manifest_file="name: my-agent\nversion: 1.0\n...", environment_config="environment: production\nnamespace: agents\n...", build_id="build_abc123", ) print(f"Deployment created: {deployment.id} (status: {deployment.status})") ``` ```typescript TypeScript theme={null} const deployment = await client.deploy.create({ manifest_file: "name: my-agent\nversion: 1.0\n...", environment_config: "environment: production\nnamespace: agents\n...", build_id: "build_abc123", }); console.log(`Deployment created: ${deployment.id} (status: ${deployment.status})`); ``` Instead of a `build_id`, you can supply `image_name` and `image_tag` directly to deploy a pre-existing image. To create a **preview deployment** (ephemeral, with automatic teardown), add the following optional parameters: | Parameter | Description | | --------------- | ---------------------------------------------------------------- | | `preview` | Set to `true` to create a preview deployment | | `preview_label` | A label for the preview (e.g., `pr-482`). Maximum 30 characters. | | `expires_at` | ISO 8601 timestamp for when the preview should be torn down | ### Get deployment status Poll `GET /v5/agentex/deployments/{deployment_id}` to check the current status and view deployment events. ```bash curl theme={null} curl -s $SGP_CLIENT_BASE_URL/v5/agentex/deployments/$DEPLOYMENT_ID \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ```python Python theme={null} deployment = client.deploy.retrieve("deploy_xyz789") print(f"Status: {deployment.status}") ``` ```typescript TypeScript theme={null} const deployment = await client.deploy.retrieve("deploy_xyz789"); console.log(`Status: ${deployment.status}`); ``` ### Delete a deployment To tear down a deployment and release its cluster resources: ```bash theme={null} curl -X DELETE $SGP_CLIENT_BASE_URL/v5/agentex/deployments/$DEPLOYMENT_ID \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ## Polling pattern Builds and deployments are asynchronous operations. After creating either resource, poll the status endpoint until the resource reaches a terminal state. **Build statuses:** `queued` → `running` → `success` | `failed` | `cancelled` | `timed_out` | `error` **Deploy statuses:** `pending` → `in_progress` → `healthy` | `failed` | `cancelled` ```bash curl theme={null} # Poll a build until it reaches a terminal state while true; do STATUS=$(curl -s $SGP_CLIENT_BASE_URL/v5/builds/$BUILD_ID \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ | jq -r '.status') echo "Build status: $STATUS" case $STATUS in success|failed|cancelled|timed_out|error) break ;; esac sleep 5 done ``` ```python Python theme={null} import time build = client.build.retrieve("build_abc123") terminal_statuses = {"success", "failed", "cancelled", "timed_out", "error"} while build.status not in terminal_statuses: time.sleep(5) build = client.build.retrieve("build_abc123") print(f"Build status: {build.status}") if build.status != "success": raise RuntimeError(f"Build ended with status: {build.status}") ``` ```typescript TypeScript theme={null} const terminalStatuses = new Set(["success", "failed", "cancelled", "timed_out", "error"]); let build = await client.build.retrieve("build_abc123"); while (!terminalStatuses.has(build.status)) { await new Promise((r) => setTimeout(r, 5000)); build = await client.build.retrieve("build_abc123"); console.log(`Build status: ${build.status}`); } if (build.status !== "success") { throw new Error(`Build ended with status: ${build.status}`); } ``` A polling interval of 5 seconds is a reasonable default. Builds typically take 2-10 minutes depending on image complexity. Deployments typically take 1-5 minutes depending on image size and cluster capacity. ## Log streaming Build logs and deployment logs use different retrieval mechanisms. ### Build logs (SSE) Build logs are streamed via Server-Sent Events (SSE) from `GET /v5/builds/{build_id}/logs`. Each event contains a log line from the image build process. ```bash theme={null} curl -N $SGP_CLIENT_BASE_URL/v5/builds/$BUILD_ID/logs \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` The response is a stream of `text/event-stream` data. Each line is a log entry from the build. The stream closes when the build completes. ### Deployment logs (cursor-based pagination) Deployment logs are retrieved with cursor-based pagination from `GET /v5/agentex/deployments/{deployment_id}/logs`. Each response includes a page of log lines, a `next_cursor` for fetching the next page, and a `has_more` flag. ```bash curl theme={null} # Fetch the first page of deployment logs CURSOR="" while true; do RESPONSE=$(curl -s "$SGP_CLIENT_BASE_URL/v5/agentex/deployments/$DEPLOYMENT_ID/logs?limit=100&cursor=$CURSOR" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID") echo "$RESPONSE" | jq -r '.lines[]' HAS_MORE=$(echo "$RESPONSE" | jq -r '.has_more') if [ "$HAS_MORE" != "true" ]; then break fi CURSOR=$(echo "$RESPONSE" | jq -r '.next_cursor') done ``` ```python Python theme={null} cursor = None while True: params = {"limit": 100} if cursor: params["cursor"] = cursor response = client.deploy.logs( "deploy_xyz789", **params, ) for line in response.lines: print(line) if not response.has_more: break cursor = response.next_cursor ``` ```typescript TypeScript theme={null} let cursor: string | undefined; while (true) { const response = await client.deploy.logs("deploy_xyz789", { limit: 100, cursor, }); for (const line of response.lines) { console.log(line); } if (!response.has_more) break; cursor = response.next_cursor; } ``` ## Listing and filtering Use the list endpoints to retrieve builds and deployments for an agent, filtered by common criteria. ### List builds Retrieve builds filtered by agent name, with control over result count and sort order. ```bash curl theme={null} curl -s "$SGP_CLIENT_BASE_URL/v5/builds?agent_name=my-agent&limit=10&sort_order=desc" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ```python Python theme={null} builds = client.build.list( agent_name="my-agent", limit=10, sort_order="desc", ) for build in builds: print(f"{build.id}: {build.status} ({build.image_tag})") ``` ```typescript TypeScript theme={null} const builds = await client.build.list({ agent_name: "my-agent", limit: 10, sort_order: "desc", }); for (const build of builds) { console.log(`${build.id}: ${build.status} (${build.imageTag})`); } ``` ### List deployments Retrieve deployments, optionally filtered by preview label to find the latest deployment for a specific branch or PR. ```bash curl theme={null} # Get the latest deployment for a specific PR curl -s "$SGP_CLIENT_BASE_URL/v5/agentex/deployments?preview_label=pr-482&limit=1" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ```python Python theme={null} deployments = client.deploy.list( preview_label="pr-482", limit=1, ) if deployments: latest = deployments[0] print(f"Latest PR deploy: {latest.id} ({latest.status})") ``` ```typescript TypeScript theme={null} const deployments = await client.deploy.list({ preview_label: "pr-482", limit: 1, }); if (deployments.length > 0) { const latest = deployments[0]; console.log(`Latest PR deploy: ${latest.id} (${latest.status})`); } ``` ### Undeployed builds To find agents that have completed builds but no healthy deployment, use the undeployed builds endpoint: ```bash theme={null} curl -s $SGP_CLIENT_BASE_URL/v5/builds/undeployed \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` This is useful for identifying agents that may have been built but never deployed, or agents whose deployments have failed and need attention. ## API and SDK reference For full request and response schemas, see the generated API reference and SDK documentation: **API reference:** * [Agentex Cloud Build](/reference/v5/agentex-cloud-build): endpoints for creating, listing, polling, and cancelling builds * [Agentex Cloud Deploy](/reference/v5/agentex-cloud-deploy): endpoints for creating, listing, inspecting, and deleting deployments * [SGP Cloud Secrets](/reference/v5/sgp-cloud-secrets): endpoints for managing account-level secrets **SDK reference:** * [Python SDK](https://sgp-dev.stldocs.app/api/python): `scale_gp_beta` package (`client.build`, `client.deploy`, `client.secrets`) * [TypeScript SDK](https://sgp-dev.stldocs.app/api/typescript): `scale-gp` package (`client.build`, `client.deploy`, `client.secrets`) The Build and Deploy API does not support webhooks or push notifications. You must poll the status endpoints to track build and deployment progress. There is no callback mechanism to notify your system when a build completes or a deployment becomes healthy. ## Next steps * [sgpctl CLI](/docs/v5/agents/agentex/cloud-build-and-deploy/sgpctl): the same operations from the terminal with a simpler interface * [CI/CD with Gitea Actions](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd): automate builds triggered by git push * [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets): manage credentials via API/SDK or the dashboard * [Preview Deployments](/docs/v5/agents/agentex/cloud-build-and-deploy/preview-deployments): ephemeral deployments for branch and PR testing # CI/CD with Gitea Actions Source: https://docs.gp.scale.com/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd Automate agent builds and deployments with the provided Gitea Actions workflow templates. ## Overview The agentex-agents repository template ships with three Gitea Actions workflows that automate the build and deploy lifecycle for your agents. These workflows use the SGP Build and Deploy API to package your agent source code into container images and deploy them to SGP-managed Kubernetes clusters. Out of the box, merging to `main` triggers an automatic build and deploy. Pushes to feature branches trigger build-only validation. You can also trigger builds and deploys manually via workflow dispatch. You do not need to write these workflows from scratch. The template repository includes them pre-configured. Your responsibility is to understand when each workflow runs, how to configure your agent directories, and how to set up the required secrets. ## How it works When you push code that changes an agent, the CI/CD pipeline detects the change, builds a container image, and (on the main branch) deploys it. The following diagram shows the end-to-end flow for a merge to main: ```mermaid theme={null} flowchart LR A[Push to main] --> B[Detect changed agents] B --> C[Package build context] C --> D[POST /v5/builds] D --> E[Poll build status] E --> F{Build succeeded?} F -- Yes --> G[POST /v5/agentex/deployments] F -- No --> H[Fail workflow] G --> I[Poll deploy status] I --> J{Deploy healthy?} J -- Yes --> K[Agent serving traffic] J -- No --> L[Fetch logs & fail] ``` The pipeline auto-detects which agents changed by running `git diff` against the previous commit. Only agents with modified files are built, so a single push that touches multiple agents triggers parallel builds for each one. ## The three workflows The template repository includes three workflow files under `.gitea/workflows/`. Each serves a different purpose in the development lifecycle. | Workflow | Trigger | Builds? | Deploys? | Use case | | ------------------------------------- | ----------------------------------------------- | ------- | -------- | ------------------------------------------- | | `cloud-build.yaml` | Push to non-main branches (agent paths) | Yes | No | Validate builds on feature branches | | `cloud-build-and-deploy-on-push.yaml` | Push to `main` (agent paths) or manual dispatch | Yes | Yes | Build and deploy on merge to main | | `cloud-deploy.yaml` | Manual dispatch or workflow call | No | Yes | Deploy a specific image version or rollback | ### cloud-build.yaml: build on feature branches This workflow triggers on pushes to any branch **except main** when files under `teams/**/agents/**` change. It auto-detects which agents were modified, packages the build context for each one, and submits a build request to the SGP Build API via `POST /v5/builds`. The workflow polls the build status until it succeeds or fails, with a 600-second timeout. This workflow does not deploy. Its purpose is to validate that your agent builds successfully before you merge. ### cloud-build-and-deploy-on-push.yaml: build and deploy on main This workflow triggers when files under `teams/**/agents/**` change on the main branch, or via manual dispatch. It builds container images for all changed agents and then deploys them. For each agent, the workflow: 1. Packages the build context based on the agent's `manifest.yaml` build configuration 2. Submits the build to `POST /v5/builds` and polls until complete 3. Reads the agent's `manifest.yaml` and `environments.yaml` 4. Submits a deployment to `POST /v5/agentex/deployments` with the resulting `build_id` 5. Polls the deployment status until it reaches `healthy` or fails The workflow uses a `resolve_inputs.py` helper script to derive the image name from the `agent.name` field in the manifest and to auto-select the environment from `environments.yaml` (auto-selected when only one environment is defined, which is the typical case). ### cloud-deploy.yaml: standalone deploy This workflow is manual-only. It deploys an existing image without rebuilding. You can trigger it via manual dispatch or call it from another workflow using `workflow_call`. Inputs include: * `agent-path` (required): path to the agent directory * `agent-environment` (required): environment key from `environments.yaml` * `build-id` OR `image-name` + `image-tag`: specify either a previous build ID or a direct image reference The workflow submits `POST /v5/agentex/deployments`, polls until the deployment is healthy, and fetches deploy logs on failure. Use the standalone deploy workflow for rollbacks. If a deployment fails, you can redeploy the previous known-good build by providing its `build-id`. ## Workflow runs After a workflow triggers, you can monitor its progress in the Gitea Actions UI. Each workflow run shows the individual steps, their status, and logs for each stage of the build and deploy process. Gitea workflow run detail page showing build and deploy steps The workflow run page displays: * Each step in the pipeline with pass/fail indicators * Timing information for build and deploy phases * Console output for debugging failed steps If a build or deployment fails, expand the failed step to see the error output. Common failures include Dockerfile syntax errors, missing dependencies, and secret reference mismatches. ## Manual dispatch You can trigger a build and deploy for a specific agent without pushing code by using the manual dispatch form in Gitea Actions. Gitea manual dispatch form with input fields for agent path and configuration To trigger a manual dispatch: 1. Navigate to the **Actions** tab in your Gitea repository 2. Select the `cloud-build-and-deploy-on-push.yaml` workflow (or `cloud-deploy.yaml` for deploy-only) 3. Click **Run workflow** 4. Fill in the inputs: * **agent-path** (required): the path to your agent directory (for example, `teams/my-team/agents/my-agent`) * **image-name** (optional): overrides the image name derived from `manifest.yaml`. Should match the `agent.name` in your manifest. * **agent-name** (optional): overrides the agent name. Should match `image-name` (the `resolve_inputs.py` helper sets both to the same value by default). * **image-tag** (optional): overrides the auto-generated tag. Use a version number (e.g., `v1.2.0`) or a commit SHA. Defaults to the current commit SHA. * **agent-environment** (optional): must match an environment key in your `environments.yaml`. Auto-selected when only one environment is defined. 5. Click **Run workflow** to start the pipeline Manual dispatch is useful during initial setup, testing, or when you need to deploy a specific image tag. For routine development, merging to main triggers the build and deploy automatically. ## Repository structure The CI/CD workflows expect a specific directory layout. Each agent lives in its own directory under `teams//agents//` and contains the configuration files the workflows need. ``` teams/ my-team/ agents/ my-agent/ manifest.yaml # Build and agent configuration environments.yaml # Deployment settings (namespace, resources, etc.) Dockerfile # Container build instructions .dockerignore # Files to exclude from build context src/ main.py # Agent source code requirements.txt # Python dependencies ... another-agent/ manifest.yaml environments.yaml Dockerfile src/ ... ``` The workflows detect changes at the `teams/**/agents/**` path level. When you push a commit that modifies any file inside an agent directory, the corresponding workflow picks up that agent for building. ## manifest.yaml The `manifest.yaml` file defines your agent's identity, build context, credentials, and deployment defaults. It is the primary configuration file the CI/CD workflows read when building and deploying your agent. ```yaml theme={null} kind: Agent build: context: # Root directory for the build context, relative to the agent directory root: ../ # Paths to include in the build context (relative to root) include_paths: - golden_agent # Path to the Dockerfile (relative to root) dockerfile: golden_agent/Dockerfile # Path to the .dockerignore file (relative to root) dockerignore: golden_agent/.dockerignore agent: # Agent Communication Protocol type: "async" or "sync" acp_type: async # Unique agent name (used as the image name by default) name: golden-agent # Human-readable description shown in the SGP dashboard description: Generic Claude Agent SDK harness. # Temporal workflow configuration (for async agents) temporal: enabled: true workflows: - name: GoldenAgentWorkflow queue_name: golden_agent_queue # Secrets injected as environment variables at runtime credentials: - env_var_name: ANTHROPIC_API_KEY secret_name: anthropic-api-key secret_key: api-key deployment: image: # Left empty; populated by the CI/CD pipeline repository: "" tag: "latest" imagePullSecrets: - name: agentex-acr-pull-secret global: replicaCount: 1 resources: requests: cpu: "500m" memory: "1Gi" limits: cpu: "1000m" memory: "2Gi" ``` ### Key fields **`build.context`** controls what files are sent to the cloud build service. Set `root` to the common ancestor directory, then use `include_paths` to specify which subdirectories to include. The `dockerfile` and `dockerignore` paths are relative to `root`. **`agent`** defines the agent's identity and runtime configuration: * `acp_type` determines whether the agent uses synchronous request-response (`sync`) or asynchronous task-based (`async`) communication. * `name` is used as the default image name and must be unique within your account. * `temporal` configures Temporal workflow settings for async agents. Each workflow entry specifies the workflow class name and task queue. * `credentials` maps secrets stored in SGP to environment variables in the running container. Each entry references a secret by name and key, and injects it as the specified environment variable. See [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets) for how to create and manage these secrets. **`deployment`** sets default resource requests and replica counts. These values can be overridden in `environments.yaml`. The `deployment.image.repository` field is intentionally left empty. The CI/CD pipeline populates it automatically with the managed registry path during the build phase. ## environments.yaml The `environments.yaml` file defines deployment settings for your agent's target environment: the Kubernetes namespace, authentication principal, resource limits, and Helm value overrides. The file has a top-level `environments` map with a named key for each environment. In practice, you define one environment per agent. The key name (e.g., `dev.aws` below) is arbitrary; the CI/CD pipeline's `resolve_inputs.py` helper auto-selects it when only one environment is defined. ```yaml theme={null} schema_version: "v1" environments: dev.aws: environment: dev kubernetes: namespace: "sgp-golden-agent" auth: principal: user_id: ac48a4a5-ca7f-4fcd-9724-f08dbc6b081f account_id: 69c69407ee5d19e1dce57d57 helm_overrides: command: - uvicorn - project.acp:acp - --host - 0.0.0.0 - --port - "8000" replicas: 1 resources: requests: cpu: 500m memory: 1Gi limits: cpu: 1000m memory: 2Gi temporal-worker: enabled: true replicaCount: 1 resources: requests: cpu: 500m memory: 1Gi limits: cpu: 1000m memory: 2Gi env: - name: SGP_ACCOUNT_ID value: "69c69407ee5d19e1dce57d57" - name: SGP_CLIENT_BASE_URL value: "http://egp-api-backend.egp.svc.cluster.local:80/public" ``` ### Key fields **`kubernetes.namespace`** determines where the agent's pods, services, and other resources are created. **`auth.principal`** provides the `user_id` and `account_id` used to authenticate API calls. These must correspond to a valid SGP service account with permissions to create deployments. **`helm_overrides`** lets you customize the container command, resource requests and limits, replica counts, Temporal worker configuration, and environment variables. Values specified here take precedence over the defaults in `manifest.yaml`. The `env` field uses array format (`[{name: X, value: Y}]`) for environment variables injected into the running container. ## Required secrets The CI/CD workflows authenticate with the SGP API using secrets stored in your Gitea repository's Actions settings. You must configure the following secrets before the workflows can run. | Secret | Description | | ---------------- | ------------------------------------------------------------ | | `SGP_API_KEY` | API key for authenticating with the SGP Build and Deploy API | | `SGP_ACCOUNT_ID` | Your SGP account identifier | To add these secrets: 1. Navigate to your Gitea repository settings 2. Go to **Actions** > **Secrets** 3. Add `SGP_API_KEY` with your SGP API key value 4. Add `SGP_ACCOUNT_ID` with your account ID The workflows use the in-cluster API base URL `http://egp-api-backend.egp.svc.cluster.local/public` by default. This is pre-configured in the workflow files and does not require a separate secret. If your cluster uses a different service address, update the `SGP_BASE_URL` variable in the workflow files directly. These repository-level secrets are different from the agent-level secrets defined in `manifest.yaml`. Repository secrets authenticate the CI/CD pipeline itself, while agent secrets (in the `credentials` section of `manifest.yaml`) are injected into the running agent container at runtime. ## Adding a new agent To add a new agent to the CI/CD pipeline: 1. **Create the agent directory** under your team's path: ```bash theme={null} mkdir -p teams/my-team/agents/my-new-agent/src ``` 2. **Add a `manifest.yaml`** with your agent's build context, name, and configuration. Use the example above as a starting point and update the `agent.name`, `build.context` paths, and `credentials` section for your agent. 3. **Add an `environments.yaml`** with your deployment target configuration. Set the `namespace`, `auth.principal`, and any `helm_overrides` for your environment. 4. **Add a `Dockerfile`** and `.dockerignore` at the paths referenced in your `manifest.yaml`. The Dockerfile should produce a container that runs your agent. 5. **Add your agent source code** under `src/` or wherever your Dockerfile expects it. 6. **Push to a feature branch** to trigger `cloud-build.yaml`. Verify that the build completes successfully in the Gitea Actions UI. 7. **Merge to main** to trigger `cloud-build-and-deploy-on-push.yaml`, which builds the agent and deploys it automatically using the configuration in `environments.yaml`. 8. **Verify the deployment** in the SGP dashboard. Navigate to your agent's detail page to confirm the deployment status is `healthy`. Ensure your `manifest.yaml` references only secrets that have already been created in SGP. A deployment will fail if it references a secret that does not exist. See [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets) for setup instructions. ## Next steps * [API and SDK](/docs/v5/agents/agentex/cloud-build-and-deploy/api): access builds and deployments programmatically * [sgpctl CLI](/docs/v5/agents/agentex/cloud-build-and-deploy/sgpctl): build, deploy, and inspect agents from the terminal * [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets): create and rotate credentials referenced in `manifest.yaml` # Cloud Build and Deploy Source: https://docs.gp.scale.com/docs/v5/agents/agentex/cloud-build-and-deploy/overview Build container images and deploy Agentex agents to SGP-managed Kubernetes clusters from the UI, CLI, CI/CD, or API. ## Overview Cloud Build and Deploy is the SGP-managed pipeline for packaging your Agentex agent code into a container image and deploying it to a Kubernetes cluster. You push your agent source code, SGP builds a container image in the cloud, and deploys it to a managed cluster where it becomes available for traffic. This removes the need to manage Docker registries, Helm charts, or Kubernetes resources directly. SGP handles image building, artifact storage, Helm releases, health checks, and traffic routing. You interact with the system through the SGP dashboard UI, the `sgpctl` CLI, CI/CD pipelines (Gitea Actions), or the Build and Deploy REST API. Cloud Build and Deploy supports both long-lived production deployments and ephemeral preview deployments for branch and PR testing. Builds and deployments are tracked with status history, event timelines, and container logs in the SGP dashboard. The system is built around two configuration files in your agent repository: `manifest.yaml` (what to build and how to deploy) and `environments.yaml` (where to deploy). These files give you declarative control over the build and deploy lifecycle without writing infrastructure code. ## How it works The following diagram shows the end-to-end flow from pushing code to serving traffic: ```mermaid theme={null} sequenceDiagram participant Dev as Developer participant CI as CI/CD (Gitea Actions) participant Build as SGP Cloud Build participant Deploy as SGP Deploy Service participant K8s as Managed K8s Cluster Dev->>CI: Push agent code CI->>CI: Package build context CI->>Build: POST /v5/builds Build->>Build: Build container image Build-->>CI: Build complete (image reference) CI->>Deploy: POST /v5/agentex/deployments Deploy->>K8s: Helm release with built image K8s->>K8s: Pods start, health checks pass K8s-->>Deploy: Agent healthy Deploy-->>Dev: Traffic routed to agent ``` The pipeline has two phases: 1. **Build phase**: Your CI pipeline (or a manual CLI/API call) sends the agent source code to the SGP Build Service via `POST /v5/builds`. SGP builds a container image using the instructions in your `manifest.yaml`, including the base image, build arguments, and any referenced secrets. The resulting image is stored in a managed container registry. You do not need to configure or authenticate with the registry yourself. 2. **Deploy phase**: A deployment is created via `POST /v5/agentex/deployments`. SGP generates a Helm release targeting the Kubernetes cluster and namespace specified in your `environments.yaml`. The deploy service monitors pod scheduling, image pulls, container startup, and health check responses. When all pods report healthy, SGP routes traffic to the new deployment and marks it as `healthy`. By default, the CI/CD pipeline runs both phases automatically on merge to main. On feature branches, only the build phase runs. You can also trigger deployments manually from the UI, CLI, or API. You can redeploy a previous build without rebuilding. A single build can have multiple deployment iterations (for example, if a deployment fails and you redeploy), but all deployments from a given build target the same environment. ## Access methods You can interact with Cloud Build and Deploy through four interfaces. Each supports the full build and deploy lifecycle. | Interface | Best for | Details | | ------------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **SGP Dashboard UI** | Visual monitoring, manual deployments, debugging | Point-and-click access to builds, deployments, logs, and events. No setup required beyond dashboard access. | | **CI/CD (Gitea Actions)** | Automated builds and deploys, team workflows | Build on every push, build and deploy on merge to main. The recommended approach for production workflows. | | **`sgpctl` CLI** | Terminal workflows, scripting, debugging | Command-line tool for creating builds, deploying, inspecting status, and tailing logs. Useful for ad-hoc operations and shell scripts. | | **API and SDK** | Programmatic access, custom integrations | REST API and Python/TypeScript SDKs for full control over builds and deployments. Use when you need to integrate with custom tooling or orchestration systems. | All four interfaces operate on the same underlying resources. A build started from the CLI appears in the dashboard UI, and a deployment created from CI/CD can be inspected via the API. In practice, most teams use CI/CD for automated builds and deploys on main, and supplement with the CLI for debugging and the dashboard for monitoring. ## Typical workflow A typical development cycle looks like this: 1. **Develop locally**: Write and test your agent code on your local machine. Use the Agentex local development tools to verify behavior before pushing. 2. **Push to a feature branch**: The CI/CD pipeline triggers a build-only workflow to validate your agent compiles and packages correctly. 3. **Merge to main**: On merge, the CI/CD pipeline builds the agent and deploys it automatically using the configuration in `environments.yaml`. 4. **Monitor**: The build and deployment appear on the agent detail page in the SGP dashboard. Build logs stream in real-time, and deployment events show rollout progress. 5. **Verify**: Check the deployment detail page for health status. For preview deployments (created separately via the UI or API/SDK), use the dedicated preview endpoint. 6. **Iterate**: If the deployment fails, use the events timeline and logs to diagnose the issue, fix the code, and push again. ## Prerequisites Before using Cloud Build and Deploy, ensure the following are in place: * **An Agentex agent repository**: Your agent code must be in a Gitea repository accessible to SGP. See the [Agentex agent types](/docs/v5/agents/agentex/overview) documentation if you have not yet created an agent. * **A `manifest.yaml` file**: Must exist at the root of your agent repository. Defines how SGP builds and deploys your agent. * **An `environments.yaml` file**: Defines the target environment (cluster, namespace, resource limits) for your deployments. * **Account-level secrets (if needed)**: If your agent requires API keys or other credentials, these must be created in the SGP secrets store before your first deployment. See [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets). * **SGP API access**: You need a valid SGP API key with permissions to create builds and deployments. This is configured in your CI/CD pipeline or CLI. If you do not yet have an agent repository, follow the [Agentex agent builder guide](/docs/v5/agents/agentex/types/agentic-agent) to scaffold a new agent project, then return here to set up the build and deploy pipeline. ## Key concepts The following concepts are central to how Cloud Build and Deploy works. ### Build A build represents a single container image build from your agent's source code. When you trigger a build, SGP packages the build context (your source files, `manifest.yaml`, and any referenced configuration), sends it to the build service, and produces a tagged container image. Each build is associated with a specific git commit and branch, providing full traceability from source to artifact. Builds progress through the following statuses: ```mermaid theme={null} stateDiagram-v2 [*] --> queued queued --> running queued --> cancelled running --> success running --> failed running --> cancelled running --> timed_out running --> error ``` | Status | Description | | ----------- | ------------------------------------------------------- | | `queued` | Build request accepted, waiting for capacity | | `running` | Image build is in progress | | `success` | Image built and stored in the managed registry | | `failed` | Build failed due to an error in your code or Dockerfile | | `cancelled` | Build was cancelled by a user or automation | | `timed_out` | Build exceeded the maximum allowed duration | | `error` | An internal error occurred during the build | ### Deployment A deployment is a Helm release of a built container image to a Kubernetes cluster. Each deployment creates or updates the Kubernetes resources needed to run your agent: pods, services, ingress rules, and any associated config maps or secrets. The target namespace and resource configuration come from your `environments.yaml`. Deployments progress through the following statuses: ```mermaid theme={null} stateDiagram-v2 [*] --> pending pending --> in_progress pending --> cancelled in_progress --> healthy in_progress --> failed in_progress --> cancelled ``` | Status | Description | | ------------- | ---------------------------------------------------------------- | | `pending` | Deployment created, not yet started | | `in_progress` | Helm release is being applied to the cluster | | `healthy` | Pods are running and passing health checks | | `failed` | Deployment failed (pod crash, resource limits, misconfiguration) | | `cancelled` | Deployment was cancelled before completion | ### Preview deployment A preview deployment is an ephemeral deployment with an automatic TTL (time-to-live) teardown, designed for branch and PR testing. When you create a preview deployment, SGP provisions a dedicated endpoint where you can test the agent version built from your feature branch without affecting the production deployment. Preview deployments are automatically cleaned up when: * The configured TTL expires (default: 8 hours) * You explicitly tear down the deployment This makes preview deployments well-suited for code review workflows where reviewers need a live agent to test against. See [Preview Deployments](/docs/v5/agents/agentex/cloud-build-and-deploy/preview-deployments) for configuration details. ### Configuration files Two YAML files in your agent repository control the build and deploy lifecycle: * **`manifest.yaml`**: defines your agent's identity, build context, credentials, and deployment defaults. See the [CI/CD guide](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd#manifestyaml) for a full annotated reference. * **`environments.yaml`**: defines the target environment (cluster, namespace, resource limits, Helm overrides). Read at deploy time, not build time, so you can change settings without rebuilding. See the [CI/CD guide](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd#environmentsyaml) for details. Your `manifest.yaml` can reference account-level [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets) that are injected as environment variables at runtime. ## The SGP dashboard The SGP dashboard provides a visual interface for monitoring builds and deployments. Key pages include: * **Agents page**: lists all registered agents with their current deployment status. Access it from **Agents** in the left navigation. * **Agent detail page**: shows build history, active deployment, and git metadata for a specific agent. * **Build detail page**: shows all deployments created from a build, organized by type (production, preview, in-progress). * **Deployment detail page**: provides the events timeline, container logs, status transitions, and configuration summary for a deployment. See the [UI walkthrough](/docs/v5/agents/agentex/cloud-build-and-deploy/ui-walkthrough) for step-by-step flows through these pages. ## What's next This overview covers the core concepts and the UI surfaces for monitoring builds and deployments. The remaining guides in this section walk through specific workflows in detail: | Guide | Description | | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | [UI walkthrough](/docs/v5/agents/agentex/cloud-build-and-deploy/ui-walkthrough) | Step-by-step flows for building and deploying from the SGP dashboard | | [CI/CD with Gitea Actions](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd) | Automated build and deploy pipelines triggered by git push | | [sgpctl CLI](/docs/v5/agents/agentex/cloud-build-and-deploy/sgpctl) | Build, deploy, and inspect agents from the terminal | | [API and SDK](/docs/v5/agents/agentex/cloud-build-and-deploy/api) | Programmatic access to builds and deployments via REST API and SDKs | | [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets) | Store and reference credentials securely | | [Preview Deployments](/docs/v5/agents/agentex/cloud-build-and-deploy/preview-deployments) | Ephemeral branch deploys for PR testing | If you are setting up Cloud Build and Deploy for the first time, start with the [CI/CD with Gitea Actions](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd) guide, which walks through `manifest.yaml` configuration and pipeline setup. For terminal-based workflows, the [sgpctl CLI](/docs/v5/agents/agentex/cloud-build-and-deploy/sgpctl) guide covers installation, authentication, and common commands for building, deploying, and tailing logs. # Preview Deployments Source: https://docs.gp.scale.com/docs/v5/agents/agentex/cloud-build-and-deploy/preview-deployments Spin up short-lived preview deployments per branch or PR with automatic teardown. ## Overview Preview deployments are ephemeral Helm releases designed for testing agent changes in isolation before promoting to production. Each preview gets its own unique Helm release name, so you can run multiple previews simultaneously (one per branch, PR, or experiment) without interfering with your production deployment or with each other. Previews are always time-boxed. Every preview deployment has an `expires_at` timestamp, defaulting to 8 hours from creation if you do not specify one. When the TTL expires, a background cleanup process automatically tears down the deployment and releases its cluster resources. You can also tear down a preview manually at any time, or promote it to a permanent production deployment when you are satisfied with the results. ## How previews differ from production | | Production | Preview | | ---------------------- | ----------------------------------------- | ----------------------------------------------------------- | | Helm release name | Stable name (e.g. `customer-support-bot`) | Unique per-deploy (e.g. `customer-support-bot-pr-482-a3f9`) | | Lifetime | Permanent until replaced | Ephemeral, auto-teardown at `expires_at` | | Default TTL | None | 8 hours | | `preview_label` | Not applicable | Optional grouping label (max 30 characters) | | Simultaneous instances | One active production deployment | Multiple previews can coexist | The unique Helm release name is what makes simultaneous previews possible. Production deployments reuse a stable release name, so deploying a new version replaces the previous one. Preview deployments append a deployment ID suffix to the release name, giving each preview its own set of Kubernetes resources. Preview deployments are supported through the SGP dashboard UI and the [API/SDK](/docs/v5/agents/agentex/cloud-build-and-deploy/api). The `sgpctl` CLI does not currently support creating preview deployments. ## Creating a preview from the UI From any successful build's detail page, click **Create deployment** (or use the chevron dropdown and select **Create preview deployment**). In the deploy modal, toggle **Preview deployment** to ON. Deploy modal with preview toggle enabled, showing preview label field and TTL picker When the preview toggle is enabled, two additional fields appear: * **Preview label**: an optional free-text label (max 30 characters) for grouping related previews. Use your branch name, PR number, or any identifier that helps you find this preview later. In the screenshot, the label is set to `pr-495`. * **TTL**: how long the preview should live before automatic teardown. The default is 8 hours. Click **Deploy preview** to create the deployment. The preview appears in the Deployments list on the build detail page with a **Preview** badge and an "Expires in X hours" indicator. ## Creating a preview from the API To create a preview deployment programmatically, set `preview` to `true` in your `POST /v5/agentex/deployments` request. You can optionally include a `preview_label` and a custom `expires_at` timestamp. ```bash curl theme={null} curl -X POST $SGP_CLIENT_BASE_URL/v5/agentex/deployments \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ -H "Content-Type: application/json" \ -d '{ "manifest_file": "kind: Agent\nagent:\n name: my-agent\n...", "environment_config": "schema_version: v1\nenvironments:\n dev.aws:\n environment: dev\n...", "build_id": "build_abc123", "preview": true, "preview_label": "pr-482", "expires_at": "2026-05-21T02:00:00Z" }' ``` ```python Python theme={null} from datetime import datetime, timezone, timedelta from scale_gp_beta import SGPClient client = SGPClient( api_key="your-api-key", account_id="your-account-id", ) deployment = client.deploy.create( manifest_file="kind: Agent\nagent:\n name: my-agent\n...", environment_config="schema_version: v1\nenvironments:\n dev.aws:\n environment: dev\n...", build_id="build_abc123", preview=True, preview_label="pr-482", expires_at=(datetime.now(timezone.utc) + timedelta(hours=8)).isoformat(), ) print(f"Preview created: {deployment.id} (expires: {deployment.expires_at})") ``` ```typescript TypeScript theme={null} import SGPClient from 'scale-gp'; const client = new SGPClient({ apiKey: process.env.SGP_API_KEY, accountId: process.env.SGP_ACCOUNT_ID, }); const expiresAt = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString(); const deployment = await client.deploy.create({ manifest_file: "kind: Agent\nagent:\n name: my-agent\n...", environment_config: "schema_version: v1\nenvironments:\n dev.aws:\n environment: dev\n...", build_id: "build_abc123", preview: true, preview_label: "pr-482", expires_at: expiresAt, }); console.log(`Preview created: ${deployment.id} (expires: ${deployment.expires_at})`); ``` Preview deployments always have an `expires_at` value. If you omit it, the API defaults to 8 hours from the time of creation. The timestamp must be timezone-aware (include `Z` or a UTC offset like `+00:00`) and must be in the future. Validation rules for preview-specific fields: | Field | Rule | | --------------- | --------------------------------------------------------------------------------------------------------------------------- | | `preview_label` | Can only be set when `preview` is `true`. Maximum 30 characters. Non-unique; multiple deployments can share the same label. | | `expires_at` | Can only be set when `preview` is `true`. Must be an ISO 8601 timestamp with timezone. Must be in the future. | ## Listing previews by label The `preview_label` field is a non-unique grouping label. Multiple preview deployments can share the same label, which is useful when you redeploy the same branch multiple times. To find the latest preview for a given label, query the deployments list endpoint with the `preview_label` filter and `limit=1`. ```bash curl theme={null} curl -s "$SGP_CLIENT_BASE_URL/v5/agentex/deployments?preview_label=pr-482&limit=1" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ```python Python theme={null} deployments = client.deploy.list( preview_label="pr-482", limit=1, ) if deployments: latest = deployments[0] print(f"Latest preview: {latest.id} (status: {latest.status})") ``` ```typescript TypeScript theme={null} const deployments = await client.deploy.list({ preview_label: "pr-482", limit: 1, }); if (deployments.length > 0) { const latest = deployments[0]; console.log(`Latest preview: ${latest.id} (status: ${latest.status})`); } ``` This pattern is useful in CI pipelines. After deploying a preview, your pipeline can poll this endpoint with the branch name as the label to check deployment health before running integration tests. ## Managing preview deployments Every preview deployment row in the Deployments list has a three-dot actions menu with operations specific to previews. Three-dot actions menu on a preview deployment showing View details, Copy deployment ID, Redeploy as preview, Promote to production, and Tear down preview The available actions are: ### Promote to production Select **Promote to production** to create a new permanent deployment with the same manifest and environment configuration as the preview. Promote to production confirmation dialog Promoting creates a new production deployment. The original preview deployment is not automatically torn down. It continues to run until its TTL expires or you tear it down manually. This lets you verify the production deployment is healthy before cleaning up the preview. ### Tear down preview Select **Tear down preview** to immediately delete the deployment and release its cluster resources. A confirmation dialog appears before the teardown proceeds. Tear down confirmation dialog Once torn down, the deployment transitions to a terminal state and its Kubernetes resources are removed. This action cannot be undone. If you need the same configuration again, use **Redeploy as preview** on a previous deployment or create a new preview from the build detail page. ### Redeploy as preview Select **Redeploy as preview** to open the deploy modal pre-filled with the preview's configuration, including the `preview_label` from the original deployment. This is a shortcut for redeploying the same branch after a code change without re-entering the configuration. ## TTL and expiration Every preview deployment has an `expires_at` timestamp that controls its lifetime. When you create a preview without specifying `expires_at`, the API defaults to 8 hours from the time of creation. In the SGP dashboard, preview deployments display an "Expires in X hours" badge so you can see at a glance how much time remains. As the TTL approaches, the badge counts down. When the `expires_at` time is reached, a background cleanup process automatically tears down the preview deployment and releases its cluster resources. You do not need to take any action. Expired previews are cleaned up automatically. If you need more time with a preview, create a new preview deployment with a longer TTL. You cannot extend the `expires_at` of an existing preview. The new preview can reuse the same `preview_label` so it remains easy to find via the API. Automatic teardown is handled by a background process that runs periodically. There may be a short delay between the `expires_at` timestamp and the actual resource cleanup, but the preview will stop serving traffic at expiration. ## CI/CD integration Preview deployments work well in CI/CD pipelines for automated branch testing. A typical pattern is: 1. On push to a feature branch, build the agent image 2. Deploy the image as a preview with `preview_label` set to the branch name 3. Poll the deployments endpoint to confirm the preview is healthy 4. Run integration tests against the preview endpoint 5. On merge to main, deploy to production (the preview expires naturally or can be torn down) Here is a simplified workflow snippet showing the deploy and verify steps: ```bash theme={null} # Build the JSON payload safely so YAML newlines and quotes are escaped PAYLOAD=$(jq -n \ --rawfile manifest manifest.yaml \ --rawfile env environments.yaml \ --arg build_id "$BUILD_ID" \ --arg label "$BRANCH_NAME" \ '{manifest_file: $manifest, environment_config: $env, build_id: $build_id, preview: true, preview_label: $label}') # After a successful build, deploy as preview DEPLOY_RESPONSE=$(curl -s -X POST $SGP_CLIENT_BASE_URL/v5/agentex/deployments \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ -H "Content-Type: application/json" \ -d "$PAYLOAD") DEPLOY_ID=$(echo "$DEPLOY_RESPONSE" | jq -r '.id') # Poll until the preview is healthy or failed while true; do STATUS=$(curl -s "$SGP_CLIENT_BASE_URL/v5/agentex/deployments/$DEPLOY_ID" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ | jq -r '.status') echo "Preview deploy status: $STATUS" case $STATUS in healthy) echo "Preview is live"; break ;; failed|cancelled) echo "Preview deploy failed"; exit 1 ;; esac sleep 10 done ``` Set the `preview_label` to your branch name or PR number in CI. This makes it straightforward to look up the latest preview for any branch using the list endpoint with the label filter, and it keeps the SGP dashboard organized when multiple branches have active previews. ## Next steps * [API and SDK](/docs/v5/agents/agentex/cloud-build-and-deploy/api): full request and response schemas for preview deployment fields * [CI/CD with Gitea Actions](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd): workflow templates for automated builds * [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets): store and reference credentials used during deployments # Secrets Source: https://docs.gp.scale.com/docs/v5/agents/agentex/cloud-build-and-deploy/secrets Store credentials as SGP Cloud Secrets and reference them from manifest.yaml to inject environment variables at runtime. ## Overview Agents typically need credentials to function: API keys for LLM providers, database connection strings, authentication tokens for external services. SGP provides a two-layer system for handling these securely. 1. **SGP Cloud Secrets** are account-level key-value pairs stored in your cloud provider's secret manager. You create them once, and any agent in your account can reference them. 2. **Manifest credentials** are per-agent mappings defined in `manifest.yaml` that bind an SGP Cloud Secret to an environment variable injected into the agent's container at runtime. You create secrets through the SGP dashboard or the Secrets API, then reference them by name in your agent's `manifest.yaml`. At deploy time, SGP syncs the secret value into a Kubernetes secret and mounts it as the specified environment variable in the agent's pod. Your agent code reads the environment variable as it would any other. ## How secrets flow The following diagram traces a secret from creation through to your running agent code. ```mermaid theme={null} flowchart LR A[Create secret via UI or API] --> B[Value stored in cloud secret manager] B --> C[manifest.yaml references secret_name] C --> D[Deploy triggered] D --> E[SGP syncs value to K8s secret] E --> F[Env var injected in pod] F --> G[Agent reads env var] ``` The value you provide when creating a secret is written directly to the cloud provider's secret store (such as Azure Key Vault). It is never stored in the SGP database and is never returned by any API call. At deploy time, the deploy service reads the value from the cloud secret store, creates or updates a Kubernetes secret in the target namespace, and mounts it as the environment variable specified in your `manifest.yaml`. ## Managing secrets in the UI Navigate to **Assets > Secrets** in the SGP dashboard. The secrets table lists all SGP Cloud Secrets for your account. Secrets table showing account-level secrets with key, description, created by, and modified columns Each row displays: * **Key**: the unique identifier for the secret (for example, `anthropic-api-key` or `redis-url-secret`) * **Description**: an optional note explaining what the secret is used for * **Created by**: the user who created the secret * **Modified**: when the secret was last updated From this table you can edit a secret (to update its value or description) or delete it. ## Creating a secret Click **Create Secret** at the top of the secrets table. Fill in the required fields in the dialog. Create Secret dialog with key, value, and description fields | Field | Required | Description | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Key** | Yes | A unique name for the secret. Must be lowercase alphanumeric characters, hyphens, or dots, with a maximum length of 253 characters (Kubernetes naming rules). | | **Value** | Yes | The credential value. This is encrypted and stored in the cloud secret manager. It is never displayed again after creation. | | **Description** | No | A human-readable note. Recommended so that other team members understand the secret's purpose. | Click **Create Secret** to save. The new secret appears in the table immediately and is available for agents to reference. Secret values are write-only. After creation, you can update the value but you cannot retrieve it. If you lose the original value, you must generate a new credential from the source system and update the secret. ## Deleting a secret Click the delete action on a secret's row. A confirmation dialog requires you to type the secret's key to proceed. Delete confirmation dialog requiring the secret name to be typed for confirmation Deleting a secret is irreversible. Any agent whose `manifest.yaml` references the deleted secret will fail on its next deployment because the secret can no longer be resolved. Verify that no agents depend on a secret before deleting it. ## Secrets API You can manage secrets programmatically through the SGP Cloud Secrets API. The following examples cover the most common operations. ### Create a secret ```bash cURL theme={null} curl -X POST "$SGP_CLIENT_BASE_URL/v5/sgp/secrets" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ -H "Content-Type: application/json" \ -d '{ "key": "datadog-api-key", "value": "dd-api-xxxxxxxxxxxxxxxxxxxxxxxxxx", "description": "Datadog API key for agent telemetry" }' ``` ```python Python theme={null} from scale_gp_beta import SGPClient client = SGPClient() secret = client.secrets.create( key="datadog-api-key", value="dd-api-xxxxxxxxxxxxxxxxxxxxxxxxxx", description="Datadog API key for agent telemetry", ) print(secret.id, secret.key) ``` ```typescript TypeScript theme={null} import SGPClient from 'scale-gp'; const client = new SGPClient({ apiKey: process.env.SGP_API_KEY, accountId: process.env.SGP_ACCOUNT_ID, }); const secret = await client.secrets.create({ key: "datadog-api-key", value: "dd-api-xxxxxxxxxxxxxxxxxxxxxxxxxx", description: "Datadog API key for agent telemetry", }); console.log(secret.id, secret.key); ``` The response includes the secret's `id`, `key`, `description`, and audit metadata. The `value` field is never returned. ```json theme={null} { "id": "sec_abc123", "key": "datadog-api-key", "description": "Datadog API key for agent telemetry", "cloud_secret_path": "/sgp/secrets/datadog-api-key", "created_by": "usr_def456", "created_at": "2026-05-20T14:30:00Z", "updated_by": "usr_def456", "updated_at": "2026-05-20T14:30:00Z", "object": "sgp_cloud_secret" } ``` ### Update a secret Use `PATCH /v5/sgp/secrets/{secret_id}` to rotate a value or update the description. ```bash cURL theme={null} curl -X PATCH "$SGP_CLIENT_BASE_URL/v5/sgp/secrets/sec_abc123" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" \ -H "Content-Type: application/json" \ -d '{ "value": "dd-api-yyyyyyyyyyyyyyyyyyyyyyyyyy", "description": "Datadog API key for agent telemetry (rotated May 2026)" }' ``` ```python Python theme={null} from scale_gp_beta import SGPClient client = SGPClient() secret = client.secrets.update( "sec_abc123", value="dd-api-yyyyyyyyyyyyyyyyyyyyyyyyyy", description="Datadog API key for agent telemetry (rotated May 2026)", ) print(secret.key, secret.updated_at) ``` ```typescript TypeScript theme={null} import SGPClient from 'scale-gp'; const client = new SGPClient({ apiKey: process.env.SGP_API_KEY, accountId: process.env.SGP_ACCOUNT_ID, }); const secret = await client.secrets.update("sec_abc123", { value: "dd-api-yyyyyyyyyyyyyyyyyyyyyyyyyy", description: "Datadog API key for agent telemetry (rotated May 2026)", }); console.log(secret.key, secret.updatedAt); ``` ### List secrets ```bash cURL theme={null} curl "$SGP_CLIENT_BASE_URL/v5/sgp/secrets" \ -H "x-api-key: $SGP_API_KEY" \ -H "x-selected-account-id: $SGP_ACCOUNT_ID" ``` ```python Python theme={null} from scale_gp_beta import SGPClient client = SGPClient() secrets = client.secrets.list() for secret in secrets: print(secret.key, secret.description) ``` ```typescript TypeScript theme={null} import SGPClient from 'scale-gp'; const client = new SGPClient({ apiKey: process.env.SGP_API_KEY, accountId: process.env.SGP_ACCOUNT_ID, }); const secrets = await client.secrets.list(); for (const secret of secrets) { console.log(secret.key, secret.description); } ``` For the full API schema including pagination, filtering, and delete operations, see the [SGP Cloud Secrets API reference](/reference/v5/sgp-cloud-secrets). ## Referencing secrets in manifest.yaml Once a secret exists in the SGP secrets store, you reference it from your agent's `manifest.yaml` in the `agent.credentials` array. Each entry maps an SGP Cloud Secret to an environment variable that your agent code can read. ```yaml theme={null} agent: credentials: - env_var_name: ANTHROPIC_API_KEY secret_name: anthropic-api-key secret_key: api-key - env_var_name: OPENAI_API_KEY secret_name: openai-api-key secret_key: api-key - env_var_name: DATABASE_URL secret_name: redis-url-secret secret_key: url ``` | Field | Description | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `env_var_name` | The environment variable name injected into the agent's container. Your code reads this with `os.environ["ANTHROPIC_API_KEY"]` or equivalent. | | `secret_name` | The `key` of the SGP Cloud Secret as it appears in the secrets table. Must match exactly. | | `secret_key` | The sub-key within the Kubernetes secret object. This is typically `api-key`, `token`, or `url`, depending on how the secret was provisioned. | At deploy time, SGP resolves each `secret_name` to its corresponding cloud secret, syncs the value into a Kubernetes secret in the agent's namespace, and mounts the value at the specified `secret_key` as the environment variable `env_var_name` in the pod. The `secret_key` field refers to the key within the Kubernetes secret data map, not a secondary lookup within the cloud secret store. Each SGP Cloud Secret maps to one Kubernetes secret, and `secret_key` selects which data entry within that Kubernetes secret to mount. ## Rotating a secret To rotate a credential: 1. Generate a new credential value from the source system (for example, your LLM provider's dashboard or your database admin panel). 2. Update the SGP Cloud Secret with the new value using the API (`PATCH /v5/sgp/secrets/{secret_id}`) or the **Edit** action in the secrets table UI. 3. Redeploy any agents that reference the rotated secret. The next deployment picks up the new value automatically. Existing running pods continue to use the previous value until they are replaced by a new deployment. Updating a secret does not trigger automatic redeployment. You must explicitly redeploy each affected agent. To minimize downtime during rotation, update the secret value first, then redeploy agents one at a time. If the new credential is invalid, only the redeployed agent is affected, and you can roll back by redeploying the previous build. ## Security model SGP Cloud Secrets are designed around a write-only, least-privilege model: * **Write-only values**: secret values are never returned by any API call or displayed in the UI after creation. The SGP database stores only metadata (key, description, audit fields). The actual value lives exclusively in the cloud provider's secret manager. * **Encrypted at rest**: values are stored using the cloud provider's native encryption (for example, Azure Key Vault with platform-managed keys). * **Injected as Kubernetes secrets**: at deploy time, values are synced into Kubernetes secrets and mounted as environment variables. They are not written to disk or config maps in the cluster. * **Role-based access**: only users with secret management permissions can create, update, or delete secrets. Read access to secret metadata (key and description) is available to users who can view the account's assets. * **Audit trail**: every create, update, and delete operation is recorded with the acting user and timestamp. ## Next steps * [CI/CD with Gitea Actions](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd): full `manifest.yaml` reference including the `credentials` section * [API and SDK](/docs/v5/agents/agentex/cloud-build-and-deploy/api): programmatic access to builds, deployments, and secrets * [Preview Deployments](/docs/v5/agents/agentex/cloud-build-and-deploy/preview-deployments): ephemeral deployments for branch and PR testing # sgpctl CLI Source: https://docs.gp.scale.com/docs/v5/agents/agentex/cloud-build-and-deploy/sgpctl Build, deploy, and observe Agentex agents from your terminal with sgpctl agentex-cloud. ## Overview `sgpctl` is the command-line interface for managing Agentex cloud builds and deployments. It covers the same operations available through the SGP dashboard and the API/SDK, packaged for terminal workflows. The CLI is organized under `sgpctl agentex-cloud` with two subcommand groups: * **`build`**: submit cloud builds, list builds, stream build logs, and cancel running builds. * **`deploy`**: submit deployments, list deployments, stream deploy logs, and watch rollouts in real time. Every command outputs structured data (JSON or Rich tables) that you can pipe into other tools or inspect directly. ## Setup ### Install sgpctl **Homebrew (macOS):** ```bash theme={null} brew tap scaleapi/sgpctl https://github.com/scaleapi/sgpctl brew install sgpctl ``` **Binary download (Linux/Windows):** Download the latest binary for your platform from the [sgpctl GitHub releases](https://github.com/scaleapi/sgpctl/releases/latest). **From the SGP monorepo:** If you have the SGP repository cloned locally, you can build and install from source: ```bash theme={null} cd services/sgpctl && poetry build && pipx install --force dist/sgpctl-*.whl ``` ### Configure environment variables All `sgpctl agentex-cloud` commands require three environment variables: ```bash theme={null} export SGP_API_KEY="your-api-key" export SGP_ACCOUNT_ID="your-account-id" export SGP_BASE_URL="https://api.your-sgp-instance.scale.com" ``` All three environment variables are required. Commands will fail immediately if any of them is missing. You can add these exports to your shell profile (`~/.bashrc`, `~/.zshrc`) or load them from a `.env` file in your CI pipeline. ## Build commands Build commands live under `sgpctl agentex-cloud build`. They let you submit new image builds, query build status, stream logs, and cancel running builds. ### Submit a build Package your agent source code and submit it for a cloud build: ```bash theme={null} sgpctl agentex-cloud build submit \ --manifest ./manifest.yaml \ --image-name my-agent \ --tag v1.2.0 ``` The `submit` command reads your `manifest.yaml`, packages the build context using `prepare_cloud_build_context()`, uploads the resulting tar.gz archive, and creates a build via `POST /v5/builds`. On success, the command prints the build details as JSON: ```json theme={null} { "build_id": "8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02", "agent_name": "my-agent", "status": "queued", "image_name": "my-agent", "image_tag": "v1.2.0" } ``` **Options:** | Flag | Description | | -------------------- | ------------------------------------------------------- | | `--manifest`, `-m` | Path to `manifest.yaml` (required) | | `--image-name`, `-i` | Repository name for the built image (required) | | `--tag`, `-t` | Image tag (default: `latest`) | | `--agent-name`, `-n` | Agent name override (inferred from manifest if omitted) | | `--build-arg`, `-b` | Build argument as `KEY=VALUE` (repeatable) | | `--platform`, `-p` | Target platform (e.g. `linux/amd64`) | You can pass multiple build arguments by repeating the flag: ```bash theme={null} sgpctl agentex-cloud build submit \ --manifest ./manifest.yaml \ --image-name my-agent \ --tag v1.2.0 \ --build-arg PYTHON_VERSION=3.11 \ --build-arg INSTALL_DEV_DEPS=false ``` ### Get build details Retrieve the full details of a specific build: ```bash theme={null} sgpctl agentex-cloud build get 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 ``` Returns the build object as JSON, including status, image URL, timestamps, and associated metadata. ### List builds List recent builds, optionally filtered by agent name: ```bash theme={null} sgpctl agentex-cloud build list --agent-name my-agent --limit 10 ``` Example output: | Build ID | Agent Name | Build Status | Image Name | Image Tag | Image URL | Created At | | -------------------------------------- | ---------- | ------------ | ---------- | --------- | ------------------------ | ------------------- | | `8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02` | my-agent | success | my-agent | v1.2.0 | registry.sgp.scale.com/… | 2026-05-20 14:32:10 | | `6d1b4e82-f3a9-4c8b-a2e5-9d7f1b3c8a04` | my-agent | success | my-agent | v1.1.0 | registry.sgp.scale.com/… | 2026-05-18 09:15:43 | | `2c7f9a31-d5e8-4a1f-b6c3-8e2d4f7a9b01` | my-agent | failed | my-agent | v1.0.1 | | 2026-05-17 16:22:05 | **Options:** | Flag | Description | | -------------------- | --------------------------------------- | | `--agent-name`, `-n` | Filter by agent name | | `--limit`, `-l` | Maximum number of results (default: 20) | | `--sort-order` | Sort direction: `asc` or `desc` | ### Stream build logs Stream real-time build logs for a running or completed build: ```bash theme={null} sgpctl agentex-cloud build logs 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 ``` Logs are delivered via Server-Sent Events (SSE) and print to stdout as they arrive: ``` [2026-05-20 14:32:15] Step 1/8 : FROM python:3.11-slim [2026-05-20 14:32:16] ---> Using cache [2026-05-20 14:32:16] Step 2/8 : WORKDIR /app [2026-05-20 14:32:17] Step 3/8 : COPY requirements.txt . [2026-05-20 14:32:18] Step 4/8 : RUN pip install -r requirements.txt [2026-05-20 14:32:45] Step 5/8 : COPY . . [2026-05-20 14:32:46] Step 6/8 : RUN python -m compileall . [2026-05-20 14:32:48] Step 7/8 : EXPOSE 8080 [2026-05-20 14:32:48] Step 8/8 : CMD ["python", "main.py"] [2026-05-20 14:32:50] Successfully built a1b2c3d4e5f6 [2026-05-20 14:32:52] Successfully tagged registry.sgp.scale.com/my-agent:v1.2.0 ``` The command exits when the build completes or the timeout is reached. | Flag | Description | | ----------- | ----------------------------------------------- | | `--timeout` | Maximum seconds to wait for logs (default: 300) | ### Cancel a build Cancel a build that is queued or running: ```bash theme={null} sgpctl agentex-cloud build cancel 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 ``` The build transitions to `cancelled` status. Builds that have already completed cannot be cancelled. ## Deploy commands Deploy commands live under `sgpctl agentex-cloud deploy`. They let you submit deployments, check deployment status, stream logs, and watch rollouts. ### Submit a deployment Deploy an agent using a completed build: ```bash theme={null} sgpctl agentex-cloud deploy submit \ --agent-path ./my-agent \ --env production \ --build-id 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 ``` The `submit` command reads `manifest.yaml` and `environments.yaml` from the directory specified by `--agent-path`, then creates a deployment via `POST /v5/agentex/deployments`. Alternatively, specify an image directly instead of a build ID: ```bash theme={null} sgpctl agentex-cloud deploy submit \ --agent-path ./my-agent \ --env staging \ --image-name my-agent \ --tag v1.2.0 ``` **Options:** | Flag | Description | | -------------------- | ------------------------------------------------------------------------------------- | | `--agent-path`, `-a` | Path to agent directory containing `manifest.yaml` and `environments.yaml` (required) | | `--env`, `-e` | Environment key from `environments.yaml` (required) | | `--build-id`, `-b` | Build ID to deploy | | `--image-name`, `-i` | Image repository name (alternative to `--build-id`) | | `--tag`, `-t` | Image tag (used with `--image-name`) | | `--watch`, `-w` | Enter the live TUI dashboard after submitting | You must provide either `--build-id` or both `--image-name` and `--tag`. The command will reject the request if neither or both are specified. ### Watch a deployment roll out Pass `--watch` to `deploy submit` to enter a fullscreen Rich dashboard immediately after the deployment is created: ```bash theme={null} sgpctl agentex-cloud deploy submit \ --agent-path ./my-agent \ --env production \ --build-id 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 \ --watch ``` The TUI dashboard shows three sections: a status header at the top (agent name, environment, deployment ID, current status), a Kubernetes events panel on the left, and a container logs panel on the right. Both panels update in real time as the deployment progresses. The dashboard auto-refreshes and exits automatically when the deployment reaches a terminal state (`healthy` or `failed`). Press `q` or `Ctrl+C` to exit early. ### Get deployment details Retrieve the full details and events for a specific deployment: ```bash theme={null} sgpctl agentex-cloud deploy get 4a9c1b72-e3f8-4d5a-8c2b-1f6e9d3a7b05 ``` Returns the deployment object as JSON, including status, events timeline, and configuration. ### List deployments List recent deployments: ```bash theme={null} sgpctl agentex-cloud deploy list --limit 10 ``` Example output: | ID | Agent | Status | Namespace | Created At | | -------------------------------------- | ----------- | ------- | -------------- | ------------------- | | `4a9c1b72-e3f8-4d5a-8c2b-1f6e9d3a7b05` | my-agent | healthy | prod-agents | 2026-05-20 14:33:00 | | `9e2d5f81-a3c7-4b9e-d1f4-6c8a2e5b7d03` | my-agent | healthy | staging-agents | 2026-05-19 11:20:15 | | `1b8c3a62-f4d9-4e7c-a5b8-3d9f2c6e1a08` | support-bot | failed | prod-agents | 2026-05-19 09:45:33 | **Options:** | Flag | Description | | --------------- | --------------------------------------- | | `--limit`, `-l` | Maximum number of results (default: 20) | ## Streaming logs Both build and deploy commands support log streaming, but they use different transport mechanisms. ### Build logs (SSE) Build logs stream via Server-Sent Events. The `build logs` command opens a persistent connection and prints each log line as it arrives: ```bash theme={null} sgpctl agentex-cloud build logs 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 --timeout 600 ``` The connection closes when the build completes or the timeout expires. This is useful for watching a build in progress from your terminal. ### Deploy logs (cursor-paged polling) Deploy logs use cursor-based polling. The `deploy logs` command fetches new log lines at a configurable interval: ```bash theme={null} sgpctl agentex-cloud deploy logs 4a9c1b72-e3f8-4d5a-8c2b-1f6e9d3a7b05 ``` By default, the output renders in a Rich Live TUI panel that updates in place. For CI environments or piping to a file, use the `--plain` flag: ```bash theme={null} sgpctl agentex-cloud deploy logs 4a9c1b72-e3f8-4d5a-8c2b-1f6e9d3a7b05 --plain ``` In plain mode, each log line is written to stdout as a single line with no formatting, making it suitable for CI log capture or `grep`: ``` 2026-05-20T14:33:24Z Starting application... 2026-05-20T14:33:25Z Loading model weights... 2026-05-20T14:33:28Z Model loaded in 3.2s 2026-05-20T14:33:28Z Health check endpoint ready 2026-05-20T14:33:28Z Listening on 0.0.0.0:8080 ``` **Options:** | Flag | Description | | ----------------- | ---------------------------------------------- | | `--poll-interval` | Seconds between log fetches (default: 2) | | `--plain` | Plain text output, no TUI (recommended for CI) | ## Common workflows ### Ship from laptop Build an image and deploy it in one sequence: ```bash theme={null} # Submit the build sgpctl agentex-cloud build submit \ --manifest ./manifest.yaml \ --image-name my-agent \ --tag v1.2.0 # Stream logs until the build completes sgpctl agentex-cloud build logs 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 --timeout 600 # Deploy and watch the rollout sgpctl agentex-cloud deploy submit \ --agent-path ./my-agent \ --env production \ --build-id 8f3a2c91-e4b7-4d2a-9f1c-3b5e7a8d6c02 \ --watch ``` After the build completes, the deploy command with `--watch` gives you a live view of the rollout from start to finish. ### Check what is deployed List active deployments to see which builds are currently serving traffic: ```bash theme={null} sgpctl agentex-cloud deploy list ``` To get full details on a specific deployment, including the image tag and environment configuration: ```bash theme={null} sgpctl agentex-cloud deploy get 4a9c1b72-e3f8-4d5a-8c2b-1f6e9d3a7b05 ``` ### Roll back to a previous build Redeploy a known-good build by referencing its build ID: ```bash theme={null} sgpctl agentex-cloud deploy submit \ --agent-path ./my-agent \ --env production \ --build-id 6d1b4e82-f3a9-4c8b-a2e5-9d7f1b3c8a04 \ --watch ``` This creates a new deployment using the image from the previous build. The existing deployment is replaced when the new one becomes healthy. ### Tail logs on a running deployment Stream container logs from a deployment that is already running: ```bash theme={null} sgpctl agentex-cloud deploy logs 4a9c1b72-e3f8-4d5a-8c2b-1f6e9d3a7b05 ``` Use `--plain` to capture logs in a file: ```bash theme={null} sgpctl agentex-cloud deploy logs 4a9c1b72-e3f8-4d5a-8c2b-1f6e9d3a7b05 --plain > deploy.log ``` ## Next steps * [API and SDK](/docs/v5/agents/agentex/cloud-build-and-deploy/api): programmatic access to builds and deployments * [CI/CD with Gitea Actions](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd): automate builds triggered by git push * [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets): store and reference credentials used during builds and deployments # UI Walkthrough Source: https://docs.gp.scale.com/docs/v5/agents/agentex/cloud-build-and-deploy/ui-walkthrough Step-by-step guide to building and deploying agents through the SGP web interface. ## Overview This page walks through the Cloud Build and Deploy workflow in the SGP dashboard. The navigation flow is: ``` Agents grid → Agent detail → Build detail → Deploy modal → Deployment detail ``` **Prerequisites:** * An SGP account with access to the Agents tab * At least one Agentex agent registered to your account * A `manifest.yaml` and `environments.yaml` prepared for your agent (see the [CI/CD guide](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd) for file format details) ## Viewing your agents Open the **Agents** tab from the top navigation bar. This shows all Agentex agents registered to your account. Agents grid showing registered Agentex agents with status badges and descriptions Click any agent card to open its detail page. ## Agent detail and build history The agent detail page has a metadata header at the top and the build history timeline below. Agent detail page showing header metadata and build history timeline The action buttons in the header link to the agent's live endpoint (**Open Agent**), its request traces (**View Traces**), and its performance profiler (**View Profiler**). Open Agent is only available when the agent has an active deployment. The **Build History** lists every build for this agent, most recent first. The status badges map to: | Status | Meaning | | ------------- | ----------------------------------------------------------- | | **Building** | Image build is in progress | | **Deploying** | Build succeeded, deployment is actively rolling out | | **Error** | Build failed during the image build phase | | **Inactive** | Build succeeded but is not currently deployed | | **Success** | Build completed successfully (visible on build detail page) | Click **+ Add Build** to trigger a new cloud build from the UI. Most teams use CI/CD pipelines for automated builds and reserve the UI button for ad-hoc builds during development. Click any build row to open its build detail page. ## Deploying a build The build detail page shows build metadata, build logs, and all deployments created from this build. Build detail page showing a successful build with no deployments and a Create deployment button The **Build Logs** section is collapsible and includes a search bar for filtering output. Use the build duration in the header to distinguish early failures (missing Dockerfile, bad config) from late failures (dependency errors). To deploy, click **Create deployment** in the Deployments section to open the deploy modal. Deploy modal showing preview toggle, manifest file section, and environment variables section The modal has two collapsible YAML sections: **Manifest File** (`manifest.yaml`) and **Environment Variables** (`environments.yaml`). * On your first deployment, paste your `manifest.yaml` and `environments.yaml` content into the respective sections. * On subsequent deployments, both sections pre-populate from the last deployment. You can deploy without expanding them if no changes are needed. * Enable the **Preview deployment** toggle to create an ephemeral deployment with an automatic TTL instead of a production deployment. See [Preview Deployments](/docs/v5/agents/agentex/cloud-build-and-deploy/preview-deployments). Deployments that reference missing secrets will fail during the Helm release phase. Ensure secrets are created in the SGP secret store before deploying. See [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets). ## Monitoring a deployment The build detail page lists all deployments from this build. Reload the page to see status updates. Build detail page showing multiple deployments with mixed statuses Production deployments show a gray badge; preview deployments show a purple badge with the PR number and an expiration countdown. Preview deployments are automatically torn down when the TTL expires. Click any deployment row to inspect its event timeline and logs. Deployment detail page showing metadata, deployment events timeline, and deploy logs section The **Deployment Events** section shows the Kubernetes event sequence for the rollout. A successful deployment progresses through: Scheduled → Pulling → Pulled → Created → Started → ScalingReplicaSet → ReleaseSuccessful. The **Deploy Logs** section shows container runtime output. If the deployment is healthy and the agent produces no stdout on startup, this section may show "No logs available." ### Diagnosing a failing deployment A gap in the event timeline tells you where the problem is: | Last event seen | Likely cause | | -------------------------------------------- | ------------------------------------------------------------------------- | | **Scheduled** | Image could not be pulled. Check the image reference and registry access. | | **Pulling** | Image pull is timing out. Large images or registry rate limits. | | **Started** | Health check is not passing. Check Deploy Logs for runtime errors. | | **ScalingReplicaSet** (no ReleaseSuccessful) | Helm timeout or release hook issue. | If a deployment stays in Deploying longer than expected, check Deployment Events. Missing events after Scheduled usually mean node resource constraints or image pull failures. ## Diagnosing a failed build Failed builds show a **Failed** badge on the build detail page and an **Error** badge in the Build History list (same state, different labels). Failed build detail page showing error status and build logs section The **Create deployment** button is disabled for failed builds. Check the **Build Logs** for error output. A short build duration (under 30 seconds) usually points to a configuration error (missing Dockerfile, invalid manifest) rather than a code error. Common causes: * **Dockerfile not found**: `manifest.yaml` references a path that doesn't exist in the build context * **Dependency install failure**: `pip install`, `npm install`, etc. fails * **Build context too large**: add a `.dockerignore` to exclude unnecessary files * **Invalid manifest**: syntax errors or invalid field values in `manifest.yaml` * **Missing secrets**: the manifest references a secret that doesn't exist * **Base image unavailable**: the Dockerfile's base image can't be pulled * **Timeout**: build exceeded the maximum duration If builds fail repeatedly, verify the agent builds locally first with `docker build`. This surfaces issues faster than iterating through the cloud pipeline. ## Next steps * [Preview Deployments](/docs/v5/agents/agentex/cloud-build-and-deploy/preview-deployments): ephemeral branch deployments for PR testing * [Secrets](/docs/v5/agents/agentex/cloud-build-and-deploy/secrets): store and reference credentials * [CI/CD with Gitea Actions](/docs/v5/agents/agentex/cloud-build-and-deploy/ci-cd): automate builds triggered by git push * [API and SDK](/docs/v5/agents/agentex/cloud-build-and-deploy/api): programmatic access to builds and deployments # Introduction to Agentex Source: https://docs.gp.scale.com/docs/v5/agents/agentex/overview Learn about Agentex agent types and how to build them Welcome to the Agentex client documentation. This guide will help you get started with building and interacting with Agentex agents. ## Quick Navigation * 📘 **[Sync Agent Guide →](/docs/v5/agents/agentex/types/sync-agent)** - For simple, direct interactions * 📗 **[Agentic (ACP) Agent Guide →](/docs/v5/agents/agentex/types/agentic-agent)** - For complex workflows and production apps **Not sure which to use?** Learn more about [choosing the right agent type](https://agentex.sgp.scale.com/docs/development_guides/tutorials/). # Agentic Agent Source: https://docs.gp.scale.com/docs/v5/agents/agentex/types/agentic-agent How to interact with agentic (async) Agentex agents using the Agentex SDK ## Overview Agentic agents use an **agentic ACP** architecture that: * Supports both streaming and non-streaming responses * Requires explicit task creation before sending events * Ideal for complex workflows and long-running operations ### Key API Method: `send_event()` For agentic (ACP) agents, you always use the **`send_event()`** method to communicate with the agent. This is different from sync agents, which use `send_message()` for direct communication. ```python theme={null} # Agentic agents use send_event() # 1. Create a task first (REQUIRED for agentic agents) task = await client.agents.create_task(agent_id=agent.id, params={}) # 2. Send events to the task await client.agents.send_event( agent_id=agent.id, params={ "task_id": task.id, "content": {"type": "text", "author": "user", "content": "Hello!"} } ) ``` **Key Differences from Sync Agents:** * **Task creation required** - Must create a task before sending events (sync agents don't need this) * **Event-based communication** - Send events to tasks, not direct messages to agent names * **Asynchronous processing** - Events are processed asynchronously, poll or stream for responses The event-based model enables asynchronous processing, state management across multiple turns, and complex workflow orchestration. ### ACP Types: Base vs Temporal Agentic agents can be implemented using two different backend types: * **Base ACP** - Simple event-driven architecture, suitable for learning and simple use cases * **Temporal** - Robust workflow engine for production, handles race conditions automatically **Important:** From the client's perspective, both base ACP and Temporal agents use the **exact same API** (both use `send_event()`). The choice between base and Temporal is purely a server-side implementation detail that is transparent to client code. You can switch an agent from base to Temporal (or vice versa) without changing any client code. ## Setup ### Prerequisites ```bash theme={null} pip install agentex-sdk ``` ### Environment Variables ```bash theme={null} export AGENTEX_API_KEY="your-api-key" export AGENTEX_BASE_URL="agentex-base-url" export SGP_ACCOUNT_ID="your-account-id" ``` ### Initialize the Client ```python theme={null} import asyncio from agentex import AsyncAgentex from agentex.types.text_content import TextContent # Initialize the async Agentex client client = AsyncAgentex( base_url=os.environ.get("AGENTEX_BASE_URL"), default_headers={ "x-api-key": os.enviorn.get("AGENTEX_API_KEY"), "x-selected-account-id": os.environ.get("SGP_ACCOUNT_ID") } ) ``` ## Basic Usage ### 1. Get Agent Information ```python theme={null} from agentex.types import Agent # List all agents agents = await client.agents.list() for agent in agents: print(f"Agent: {agent.name} - {agent.id} - {agent.acp_type}") # Retrieve a specific agent by name AGENT_NAME = "" # Replace with your agent name agent = await client.agents.retrieve_by_name(agent_name=AGENT_NAME) print(f"Agent ID: {agent.id}") print(f"Agent Type: {agent.acp_type}") print(f"Description: {agent.description}") ``` ### 2. Create a Task Agentic agents require a task to be created before sending events: ```python theme={null} import uuid # Create a new task create_task_response = await client.agents.create_task( agent_id=agent.id, params={ "name": f"{uuid.uuid4()}-task", "params": {} } ) task = create_task_response.result print(f"Task ID: {task.id}") print(f"Task Status: {task.status}") ``` ### 3. Send an Event ```python theme={null} # Create event content event_content = TextContent( type="text", author="user", content="Hello! Can you explain what quantum computing is?" ) # Send the event to the task event_response = await client.agents.send_event( agent_id=agent.id, params={ "task_id": task.id, "content": event_content } ) event = event_response.result print(f"Event ID: {event.id}") print(f"Event created at: {event.created_at}") ``` ### 4. Receive Messages (Polling) Since agentic agents process events asynchronously, you need to poll for messages: ```python theme={null} from datetime import datetime has_received_response = False messages = [] timeout = 30 start_time = datetime.now() # Poll for messages while not has_received_response and (datetime.now() - start_time).seconds < timeout: messages = await client.messages.list(task_id=task.id) for message in messages: content = message.content if content.author == "agent": has_received_response = True break await asyncio.sleep(1) # Print received messages print(f"\nReceived {len(messages)} messages:\n") for message in messages: if message.content and message.content.type == "text": print(f"[{message.content.author}]: {message.content.content}") print("-" * 80) ``` ## Streaming Messages ### Helper Function for Streaming Use the built-in helper to subscribe to task messages when testing in a local environmet: ```python theme={null} from agentex.lib.utils.dev_tools import subscribe_to_async_task_messages async def send_and_stream_event(task_id: str, user_message: str): """Send an event and stream the agent's response.""" # Send the event event_content = TextContent( type="text", author="user", content=user_message ) await client.agents.send_event( agent_id=agent.id, params={ "task_id": task_id, "content": event_content } ) print(f"User: {user_message}\n") # Subscribe to task messages messages = await subscribe_to_async_task_messages( client=client, task_id=task_id, timeout=30, max_retries=10, ) return messages # Usage await send_and_stream_event( task.id, "Can you tell me about machine learning?" ) ``` ### Custom Streaming Implementation For more control over streaming or for production environments, implement custom streaming logic: ```python theme={null} import json async def custom_send_stream_event(task_id: str, user_message: str): """Send an event and stream the agent's response with custom logic.""" # Send the event event_content = TextContent( type="text", author="user", content=user_message ) await client.agents.send_event( agent_id=agent.id, params={ "task_id": task_id, "content": event_content } ) print(f"User: {user_message}\n") print("Agent: ", end="", flush=True) # Stream events async with client.tasks.with_streaming_response.stream_events( task_id=task_id, timeout=30 ) as stream: try: response_message = "" async for task_message_update in stream.iter_lines(): if task_message_update.startswith("data: "): # Remove 'data: ' prefix task_message_update_json = task_message_update.strip()[6:] task_message_update_data = json.loads(task_message_update_json) if task_message_update_data.get("type") == "delta": delta = task_message_update_data.get("delta", {}) if delta.get("type") == "text": text_delta = delta.get("text_delta", "") response_message += text_delta print(text_delta, end="", flush=True) elif task_message_update_data.get("type") == "done": print("\n") break except Exception as e: print(f"\nError streaming messages: {e}") # Usage await custom_send_stream_event( task.id, "Can you tell me about tennis?" ) ``` ## Multi-Turn Conversations Agentic agents automatically maintain conversation history: ```python theme={null} # Create a new task for the conversation conversation_task_id = str(uuid.uuid4()) create_task_response = await client.agents.create_task( agent_id=agent.id, id=conversation_task_id, params={} ) conversation_task = create_task_response.result # Define a multi-turn conversation messages = [ "Can you tell me about machine learning?", "What are some practical applications?", "How do I get started learning it?", ] # Send each message and stream responses for message in messages: print(f"Task ID: {conversation_task.id}") await custom_send_stream_event(conversation_task.id, message) print("=" * 80 + "\n") ``` ### View Conversation History Retrieve all messages from a task: ```python theme={null} # Get all messages from the task all_messages = await client.messages.list(task_id=conversation_task.id) print("\n" + "=" * 80) print("COMPLETE CONVERSATION HISTORY") print("=" * 80 + "\n") for idx, message in enumerate(all_messages, 1): if message.content and message.content.type == "text": author = message.content.author.upper() print(f"{idx}. [{author}]") print(f"{message.content.content}\n") print("-" * 80) ``` ## Managing Tasks ### Check Task Status ```python theme={null} # Retrieve task status task_info = await client.tasks.retrieve(task_id=task.id) print(f"Task ID: {task_info.id}") print(f"Status: {task_info.status}") print(f"Status Reason: {task_info.status_reason}") print(f"Created At: {task_info.created_at}") print(f"Updated At: {task_info.updated_at}") ``` ### List All Tasks for an Agent ```python theme={null} # List tasks tasks = await client.tasks.list(agent_id=agent.id) for task in tasks: print(f"Task {task.id}: {task.status}") ``` ## State Management Agentic agents can maintain state across events: ```python theme={null} # List states for an agent states = await client.states.list(agent_id=agent.id) for state in states: print(f"State ID: {state.id}") print(f"Task ID: {state.task_id}") print(f"State Data: {state.state}") ``` # Sync Agent Source: https://docs.gp.scale.com/docs/v5/agents/agentex/types/sync-agent How to interact with synchronous (sync) Agentex agents using the Agentex SDK ## Overview Sync agents use a **sync ACP** architecture that: * Uses direct message-based communication via RPC * Returns responses with the request (synchronously) * Supports both streaming and non-streaming responses ### Key API Method: `send_message(), send_message_stream()` For sync agents, you always use the **`send_message()`** or **`send_message_stream()`** method to communicate with the agent. This is different from agentic agents, which use `send_event()` for task-based communication. ```python theme={null} # Sync agents use send_message() - no task creation needed! response = client.agents.send_message( agent_name=AGENT_NAME, params={ "content": {"type": "text", "author": "user", "content": "Hello!"} } ) ``` **Key Differences:** * **No task creation required** - You can send messages directly to the agent by name * **No event creation required** - You can send messages directly to the agent by name ## Setup ### Prerequisites ```bash theme={null} pip install agentex-sdk ``` ### Environment Variables ```bash theme={null} export AGENTEX_API_KEY="your-api-key" export AGENTEX_BASE_URL="http://agentex.agentex.azure.workspace.egp.scale.com" export SGP_ACCOUNT_ID="your-account-id" ``` ### Initialize the Client ```python theme={null} from agentex import Agentex from agentex.types import TextContent # Initialize the Agentex client client = Agentex( base_url=os.environ.get("AGENTEX_BASE_URL"), api_key="your-api-key", default_headers={ "x-api-key": os.environ.get("AGENTEX_API_KEY"), "x-selected-account-id": os.environ.get("SGP_ACCOUNT_ID"), }, ) AGENT_NAME = "" # Replace with your agent name ``` ## Basic Usage ### Non-Streaming Response Send a message and receive a complete response immediately as part of the response: ```python theme={null} # Send a message without streaming rpc_response = client.agents.send_message( agent_name=AGENT_NAME, params={ "content": { "type": "text", "author": "user", "content": "Hello! Can you explain what machine learning is?", }, "stream": False, }, ) # Extract and print the text content from the response print("User: Hello! Can you explain what machine learning is?\n") print("Agent:") for task_message in rpc_response.result: content = task_message.content if isinstance(content, TextContent): print(content.content) ``` **Output:** ``` User: Hello! Can you explain what machine learning is? Agent: Machine learning is a subset of artificial intelligence that enables systems to learn and improve from experience without being explicitly programmed... ``` ## Streaming Responses For real-time responses, use streaming mode: ```python theme={null} from agentex.types import TextDelta from agentex.types.agent_rpc_result import ( StreamTaskMessageDelta, StreamTaskMessageDone, StreamTaskMessageFull ) print("User: Can you give me a simple example of supervised learning?\n") print("Agent: ", end="", flush=True) # Stream the response for chunk in client.agents.send_message_stream( agent_name=AGENT_NAME, params={ "content": { "type": "text", "author": "user", "content": "Can you give me a simple example of supervised learning?", }, }, ): # The result is a TaskMessageUpdate task_message_update = chunk.result # Print text deltas as they arrive if isinstance(task_message_update, StreamTaskMessageDelta): delta = task_message_update.delta if isinstance(delta, TextDelta): print(delta.text_delta, end="", flush=True) elif isinstance(task_message_update, StreamTaskMessageFull): content = task_message_update.content if isinstance(content, TextContent): print(content.content) print("\n") ``` # Introduction to Agents Source: https://docs.gp.scale.com/docs/v5/agents/overview Gain an understanding of different agent types in SGP ## Agent Types There are two main types of agents in SGP: * [Agentex Agents](/docs/v5/agents/agentex/overview) * [Agent Service Agents](/docs/v5/agents/agent-service/overview) Agentex Agents are built in code and support streaming and non-streaming responses. This is the preferred way to build agents for production use and will have the most support moving forward. Agent Service Agents are built inside of SGP using a YAML configuration. They are more limited in their capabilities and will be deprecated in the future. ## View agents To view all available agents in SGP navigate to the [agents page](https://egp.dashboard.scale.com/beta/build). On this page you can see a grid or table view of all agents for your account. Metadata like the agent type, status, description, and updated time is visible. Agents grid view Clicking on an agent will take you to the agent's detail page. You can also create an Agent Service Agent by clicking the **Create Agent** button. Learn more about [Agent Service Agents Creation](/docs/v5/agents/agent-service/lifecycle/creating-agent-service-agents). # Authentication Source: https://docs.gp.scale.com/docs/v5/authentication The next step is to get your API key and account ID, which are needed to make calls to the Scale GP API and to create, modify resources in the right place. First navigate to the [SGP Admin](https://app.sgp.scale.com/admin/api-key) and grab your API key. If you do not have an account provisioned by your organization or Scale, please contact the relevant System administrator. Switch to Accounts and find the account ID. or via the UI by clicking on the "API Keys" tab in the admin dashboard. API Key menu Next, switch to [Accounts](https://app.sgp.scale.com/admin/accounts) and find the account ID for the account that you want to work with. You can also create a new account. Make sure to copy this ID to use it when testing your setup. You can also create a new account. If you have an existing account but want to add Users to it, you can click into the account and add users. Click into the account to add users. You can also change the role of a user by clicking on the user and changing the role. Change the role of a user. You can also remove a user by clicking on the remove button in the actions column. Remove a user. ## Storing Your Credentials For security and convenience when using the SDK, it's recommended to store your API key and account ID as environment variables. This prevents accidentally exposing your credentials in code repositories. ```bash macOS/Linux theme={null} export SGP_API_KEY="your-api-key-here" export SGP_ACCOUNT_ID="your-account-id-here" ``` ```bash Windows (PowerShell) theme={null} $env:SGP_API_KEY="your-api-key-here" $env:SGP_ACCOUNT_ID="your-account-id-here" ``` ```bash Windows (CMD) theme={null} set SGP_API_KEY=your-api-key-here set SGP_ACCOUNT_ID=your-account-id-here ``` The SDK will automatically use the `SGP_API_KEY` and `SGP_ACCOUNT_ID` environment variables if they are set. Otherwise, you can pass them to the `SGPClient` constructor. ```python Python theme={null} import os from scale_gp_beta import SGPClient client = SGPClient( account_id="your-account-id", api_key="your-api-key", ) ``` The `SGPClient` constructor accepts the following parameters: | Parameter | Description | Default | | ------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | | `account_id` | Your SGP account ID. Found in the [Accounts](https://app.sgp.scale.com/admin/accounts) tab of the admin dashboard. | `SGP_ACCOUNT_ID` environment variable | | `api_key` | Your SGP API key. Found in the [API Keys](https://app.sgp.scale.com/admin/api-key) tab of the admin dashboard. | `SGP_API_KEY` environment variable | | `environment` | The SGP environment to connect to. **Possible values:** `production-multitenant`, `staging`, `development`, `local`. | `production-multitenant` | | `base_url` | Override the base URL with a custom endpoint. Takes precedence over `environment`. | None | To make the environment variables persist across sessions, add the export commands to your shell configuration file (e.g., `~/.bashrc`, `~/.zshrc`, or `~/.bash_profile`). # Evaluation Group Dashboards Source: https://docs.gp.scale.com/docs/v5/evaluation-dashboards/eval-group-dashboards Compare metrics across multiple evaluations with evaluation group dashboards Evaluation Group Dashboard ## What are Evaluation Group Dashboards? Evaluation group dashboards let you aggregate and compare data across multiple evaluations in a single dashboard. Instead of viewing metrics for one evaluation at a time, you can visualize trends, compare performance, and track progress across an entire group of related evaluations. Evaluation group dashboards support all the same widget types and query language as single-evaluation dashboards, with additional features for cross-evaluation analysis. Watch the [evaluation group dashboard demo video](./overview#evaluation-group-dashboard) for a walkthrough. Dashboards use an XOR constraint -- they belong to either a single evaluation OR an evaluation group, never both. ## Creating an Evaluation Group Dashboard ### Via the UI Navigate to the Dashboards page and click "New Dashboard" In the create dialog, select the "Groups" tab and choose the evaluation group you want to create a dashboard for. * **Name**: Give your dashboard a descriptive name * **Description**: Optional description explaining the dashboard's purpose * **Tags**: Optional tags for organization and filtering Evaluation Group Dashboard Create Modal Click "Create" to save your dashboard. You can now add widgets to visualize your group data. ### Via the SDK ```python theme={null} from scale_gp_beta import SGPClient client = SGPClient( api_key="your-api-key", account_id="your-account-id", environment="development" ) # Create dashboard for an evaluation group dashboard = client.evaluation_dashboards.create( name="Cross-Evaluation Comparison", evaluation_group_id="eval-group-456", description="Compare performance across weekly evaluation runs", tags=["comparison", "weekly"] ) ``` ## Querying Across Evaluations Evaluation group dashboards extend the standard query language with fields for controlling which evaluations to include in computations. ### The `evaluation_ids` Field Add `evaluation_ids` to a query to specify which evaluations in the group to include. If omitted, all evaluations in the group are used. ```json theme={null} { "select": [ { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data" }, "alias": "avg_score" } ], "evaluation_ids": ["eval-abc", "eval-def", "eval-ghi"] } ``` This computes the average score across only the three specified evaluations, rather than all evaluations in the group. ### Per-Aggregation `evaluation_ids` Individual aggregation nodes can also specify their own `evaluation_ids`, which must be a subset of the query-level `evaluation_ids`. This allows you to compare metrics across different evaluation subsets within the same widget. ```json theme={null} { "select": [ { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data", "evaluation_ids": ["eval-abc"] }, "alias": "eval_a_avg" }, { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data", "evaluation_ids": ["eval-def"] }, "alias": "eval_b_avg" } ], "evaluation_ids": ["eval-abc", "eval-def"] } ``` Per-aggregation `evaluation_ids` must be a subset of the query-level `evaluation_ids`. The system validates this constraint and will reject queries where a per-node set includes IDs not present at the query level. ### The `_evaluation_id` Column A special `_evaluation_id` column is automatically available in evaluation group dashboard queries. This column contains the ID of the evaluation that each data row belongs to, allowing you to group or filter by evaluation source. ```json theme={null} { "select": [ {"expression": {"type": "COLUMN", "column": "_evaluation_id"}}, { "expression": {"type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data"}, "alias": "avg_score" }, { "expression": {"type": "AGGREGATION", "function": "COUNT", "column": "*"}, "alias": "item_count" } ], "groupBy": ["_evaluation_id"] } ``` This produces a row per evaluation in the group, showing the average score and item count for each. ## Per-Evaluation Selection in Widgets When creating widgets in the UI for an evaluation group dashboard, the widget creator allows you to select which evaluations to include per aggregation. This is particularly useful for: * **Metric widgets**: Compare the same metric across specific evaluations * **Chart widgets**: Compare multiple metrics across specific evaluations * **Table widgets**: Include different evaluation subsets for different columns The UI evaluation selector maps directly to the `evaluation_ids` field on aggregation nodes in the query. ## Auto-Recomputation on Group Changes When evaluations are added to or removed from an evaluation group, all dashboard widgets for that group are automatically recomputed. This ensures your dashboards always reflect the current state of the group. ### How it Works 1. **Membership change detected**: When you add or remove evaluations from a group, the system triggers an asynchronous recomputation workflow 2. **Smart `evaluation_ids` updates**: Widgets whose `evaluation_ids` covered all previous group members are automatically expanded or contracted to reflect the new membership. For example, if a group had evaluations \[A, B] and you add C, widgets covering \[A, B] are updated to \[A, B, C] 3. **Results marked as pending**: Existing widget results are marked with `computation_status: "pending"` while recomputation runs 4. **Recomputation completes**: Each widget is recomputed with the updated evaluation data, and results are updated to `computation_status: "completed"` This allows for widgets to be updated as new evaluations are added to the group. Any widgets computed across all evaluations in the group will be updated to include the new evaluation. This is particularly useful for the timeseries widget, where each evaluation is plotted as a member of the x-axis. ## Example: Cross-Evaluation Comparison Dashboard Here's a full example creating an evaluation group dashboard with multiple widget types. ### Step 1: Create the Dashboard ```python theme={null} dashboard = client.evaluation_dashboards.create( name="Weekly Agent Performance", evaluation_group_id="eval-group-weekly", tags=["weekly-review"] ) ``` ### Step 2: Add a Heading ```python theme={null} client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Performance Overview", type="heading" ) ``` ### Step 3: Add Metric Widgets per Evaluation ```python theme={null} # Average score across all evaluations in the group client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Overall Average Score", type="metric", query={ "select": [ { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data" } } ] } ) ``` ### Step 4: Add a Timeseries Showing Trends ```python theme={null} client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Score Trend Over Evaluations", type="timeseries", query={ "select": [ { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data" }, "alias": "avg_score" }, { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "accuracy_score", "source": "data" }, "alias": "avg_accuracy" } ] } ) ``` ### Step 5: Add a Table Grouped by Evaluation ```python theme={null} client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Per-Evaluation Breakdown", type="table", query={ "select": [ {"expression": {"type": "COLUMN", "column": "_evaluation_id"}}, { "expression": {"type": "AGGREGATION", "function": "COUNT", "column": "*"}, "alias": "items" }, { "expression": {"type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data"}, "alias": "avg_score" }, { "expression": { "type": "AGGREGATION", "function": "PERCENTAGE", "column": "*", "params": { "percentage_filters": { "conditions": [{"column": "overall_score", "source": "data", "operator": ">=", "value": 85}] } } }, "alias": "pass_rate" } ], "groupBy": ["_evaluation_id"] } ) ``` ## Related Documentation * [Evaluation Dashboards Overview](./overview) - Introduction to dashboards * [Getting Started](./getting-started) - Create your first dashboard * [Timeseries Widget](./widget-types/timeseries) - Ideal for group trend visualization * [Query Language](./query-language) - Complete query syntax reference * [API Reference](/reference/v5/evaluation-dashboards) - Programmatic dashboard management # Latest Snapshots Source: https://docs.gp.scale.com/docs/v5/evaluation-dashboards/eval-group-latest-snapshots View the results from the most recent evaluation in an evaluation group ## What are Latest Snapshots? A *latest snapshot* is a widget on an evaluation group dashboard that computes against only the most recently created evaluation in a group. "Most recent" is defined as the evaluation in the group with the latest `created_at` timestamp -- **not** the order in which evaluations were added to the group. ## When to use it * **Snapshot tiles** at the top of a dashboard that need to read the last run's score. * **Comparison views** where you want the latest run highlighted alongside trend visualizations of the full group. * **Always-fresh KPI widgets** that should automatically refocus on a new evaluation as soon as it's added to the group. ## Where it applies Latest snapshots are only available on widgets that issue a query. | Widget type | Latest Only available? | | ----------------------- | --------------------------------------------------------------- | | Metric (Query Value) | Yes | | Bar | Yes | | Histogram | Yes | | Donut | Yes | | Table (grouped or flat) | Yes | | Scatter | Yes | | Timeseries | Yes, but rarely useful -- collapses the chart to a single point | | Markdown, Heading | Not applicable | The toggle is **only available on evaluation group dashboards**. ## Enable via the UI Toggle **Latest only** in the widget modal when creating or editing a widget. Latest only toggle in the Add Widget modal ## Enable via the SDK Set `latest_only: true` on the query payload when creating or updating the widget. The flag is supported on both `SeriesQuery` (Bar / Histogram / Donut / Table / Scatter / Timeseries) and `MetricQuery` (Metric / Query Value). ```python theme={null} # Metric widget that always shows the average score of the most recent run in the group client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Latest Snapshot: Average Score", type="metric", query={ "select": [ { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data", }, } ], "latest_only": True, }, ) ``` Omitting `latest_only` or setting it to `false` keeps the existing behavior of computing across the full group. # Getting Started with Dashboards Source: https://docs.gp.scale.com/docs/v5/evaluation-dashboards/getting-started Step-by-step tutorial for creating your first evaluation dashboard This guide walks you through creating your first evaluation dashboard from scratch, adding widgets, and organizing your layout. You can also watch the [single evaluation dashboard demo video](./overview#single-evaluation-dashboard) for a visual walkthrough. ## Using Sample Data To follow along with this tutorial, you can use our sample evaluation dataset containing 40 agent evaluation items with realistic scores and metadata. Upload the sample data to your account and create an evaluation with it. **Download Sample Data:**
Sample dataset with 40 evaluation items across 4 agents (GPT-4, Claude-3, Gemini-Pro, Llama-3) **Data Structure:** The sample data contains evaluation items with this structure: ```json theme={null} { "id": "eval_001", "agent_name": "GPT-4-Turbo-Agent", "agent_version": "1.0", "judged_evaluation": { "overall_score": 87, "accuracy_score": 92, "relevance_score": 85, "coherence_score": 89, "helpfulness_score": 84, "fluency_score": 91 }, "timestamp": "2026-01-15T10:30:00Z", "task_type": "question_answering", "prompt_category": "technical", "response_length": 256, "model_temperature": 0.7 } ``` **Key Fields:** * `agent_name`: Model being evaluated (GPT-4-Turbo-Agent, Claude-3-Sonnet-Agent, etc.) * `*`: Nested scores (overall\_score, accuracy\_score, relevance\_score, coherence\_score, helpfulness\_score, fluency\_score) * `task_type`: Type of task (question\_answering, summarization, code\_generation, analysis, translation, creative\_writing) * `prompt_category`: Category (technical, general, business, language, creative) **How to Use:** 1. Download the CSV file 2. Create a new evaluation via the API or SDK: ```python theme={null} from scale_gp_beta import SGPClient import csv # Using api.dev-sgp.scale.com client = SGPClient( api_key="your-api-key", account_id="your-account-id", environment="development" ) # Load sample data with open('sample-evaluation.csv', 'r') as f: sample_items = csv.reader(f) # Create evaluation with sample data evaluation = client.evaluations.create( name="Agent Performance Comparison", data=sample_items ) print(f"Created evaluation: {evaluation.id}") ``` The client uses the `environment` parameter to connect to different Scale GP deployments. Available options: `"production"`, `"production-multitenant"`, `"development"`, `"staging"`, `"local"`. For custom endpoints, use `base_url` instead. 3. Follow the rest of this guide to create dashboards and widgets using this evaluation The examples throughout this guide reference fields from this sample dataset. If using your own data, adjust the column names accordingly. ## Prerequisites Before creating a dashboard, you need either: * An existing evaluation with completed results, OR * An evaluation group containing evaluations If you don't have an evaluation yet, see [Next Gen Evaluation Getting Started](../next-gen-evaluation/getting-started) to create one. ## Step 1: Create a New Dashboard ### Via the UI 1. Navigate your version of SGP ([Dev SGP](https://app.dev-sgp.scale.com/)) 2. Make sure the `evaluation-dashboards-enabled` feature flag is enabled for your account 1. ([Instructions to enable the feature flag](https://scale.atlassian.net/wiki/x/C4EgLw)) 3. Click the "Dashboards" tab 4. Click the "New Dashboard" button New Dashboard Button 5. Fill in the dashboard details: * **Name**: Give your dashboard a descriptive name (e.g., "Model Performance Overview") * **Description**: Optional description explaining the dashboard's purpose * **Tags**: Optional tags for organization and filtering * **Evaluation / Evaluation Group**: Select the evaluation or evaluation group you want to create a dashboard for * **Template** *(optional)*: Select an existing single-evaluation dashboard to copy its widget layout 6. Click "Create" to save your dashboard New Dashboard Form ### Via the SDK ```python theme={null} from scale_gp_beta import SGPClient client = SGPClient( api_key="your-api-key", account_id="your-account-id", environment="development" ) # Create dashboard for a single evaluation dashboard = client.evaluation_dashboards.create( name="Demo Dashboard", description="A demo dashboard for the demo evaluation", evaluation_id="eval-123", tags=["demo", "documentation"] ) # Create dashboard from an existing template (single-evaluation dashboards only) dashboard_from_template = client.evaluation_dashboards.create( name="Q2 Model Performance", evaluation_id="eval-456", template_dashboard_id="dash-template-abc" # Copies widget layout from this dashboard ) # Or create dashboard for an evaluation group group_dashboard = client.evaluation_dashboards.create( name="Cross-Evaluation Comparison", evaluation_group_id="eval-group-456", tags=["comparison"] ) ``` ## Step 2: Add Your First Widget (Metric) Let's add a metric widget to display the average score across all evaluation items. ### Via the UI 1. From your dashboard page, click "Add Widget" 2. Select "Query Value" as the widget type 3. Configure the widget: * **Title**: "Average Score" * **Query**: Select the average of the "score" column 4. Click "Add" Average Score Widget Form Average Score Widget Result Widget results are automatically computed when you create or update a widget. The response includes both the widget configuration and the computed result. ### Via the API ```python theme={null} # Add a metric widget showing average score widget = client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Average Score", type="metric", query={ "select": [ { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data" } } ] } ) print(f"Computed result: {widget.result.computed_result}") # Output: {'type': 'metric', 'data': 0.873} ``` ## Step 3: Add a Chart Widget (Bar Chart) Now let's add a bar chart to show score distribution across different models. ### Via the UI 1. Click "Add Widget" again 2. Select "Bar Chart" as the widget type 3. Configure the widget: * **Title**: "Score by Agent" * **Group By**: Select "agent\_name" * Under the Advanced Options * Add an aggregation, select "Average" on "overall\_score" 4. Click "Add" Score by Agent Bar Chart Form Score by Agent Bar Chart Result ### Via the SDK ```python theme={null} # Add a bar chart widget showing average score by category widget = client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Score by Agent", type="bar", query={ "select": [ { "expression": { "type": "COLUMN", "column": "agent_name", "source": "data" } }, { "expression": { "type": "AGGREGATION", "function": "AVG", "column": "overall_score", "source": "data" } } ], "groupBy": ["agent_name"] }, config={ "x_column": "agent_name" } ) ``` ## Step 4: Add Section Headers Use heading widgets to organize your dashboard into logical sections. ### Via the UI 1. Click "Add Widget" 2. Select "Heading" as the widget type 3. Configure the widget: * **Title**: "Graphs" 4. Click "Add" Header Widget Form Header Widget Result ### Via the SDK ```python theme={null} # Add a heading widget heading = client.evaluation_dashboards.widgets.create( dashboard_id=dashboard.id, title="Graphs", type="heading" ) ``` ## Step 5: Organize and Configure Layout ### Reorder Widgets Arrange widgets in your preferred order by dragging and dropping in the UI, or update the widget order via the API: ```python theme={null} # Reorder widgets - first widget appears at top client.evaluation_dashboards.update( dashboard_id=dashboard.id, widget_order=[heading.id, widget1.id, widget2.id] ) ``` For evaluation group dashboards, see the dedicated [Evaluation Group Dashboards](./eval-group-dashboards) guide for group-specific features like cross-evaluation queries, per-evaluation selection, and auto-recomputation. ## Next Steps * Learn about all available [Widget Types](./widget-types/overview) * Compare across evaluations with [Evaluation Group Dashboards](./eval-group-dashboards) * Capture [Latest Snapshots](./eval-group-latest-snapshots) of your evaluation group * Master the [Query Language](./query-language) for advanced filtering and aggregations * Explore [API Reference](/reference/v5/evaluation-dashboards) for programmatic query creation # Introduction to Evaluation Dashboards Source: https://docs.gp.scale.com/docs/v5/evaluation-dashboards/overview Create custom dashboards to visualize and analyze evaluation metrics ## What are Evaluation Dashboards? Evaluation Dashboards are customizable visualizations that allow you to analyze and monitor evaluation performance with widgets. Dashboards are a collection of metric cards, charts, and tables that can be used to provide insights into evaluation results. Evaluation Dashboards Overview ## Demo Videos ### Single Evaluation Dashboard