agent-factory: generate agent auto-deploy-application-to-developm

This commit is contained in:
2026-08-05 01:04:38 +00:00
parent 729c3591a1
commit 1eacad2e34
19 changed files with 586 additions and 0 deletions

1
.image-version Normal file
View File

@@ -0,0 +1 @@
1.0.0

25
Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ ./app/
RUN useradd -m -u 1001 appuser && \
chown -R appuser:appuser /app
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
CMD ["uvicorn", "app.agent:app", "--host", "0.0.0.0", "--port", "8080"]

0
app/__init__.py Normal file
View File

148
app/agent.py Normal file
View File

@@ -0,0 +1,148 @@
"""
AutoDeployApplicationToDevelopm Agent — auto-generated by Agent Factory.
"""
import asyncio
import logging
import os
import click
import httpx
import uvicorn
from contextlib import asynccontextmanager
from dotenv import load_dotenv
from langchain_core.rate_limiters import InMemoryRateLimiter
from langchain_openai import AzureChatOpenAI
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from app.config import (
AGENT_SELF_URL,
AZURE_OPENAI_API_KEY,
AZURE_OPENAI_API_VERSION,
AZURE_OPENAI_DEPLOYMENT,
AZURE_OPENAI_ENDPOINT,
LOG_LEVEL,
REGISTRY_URL,
)
from app.skills import AGENT_CONFIG, AUTO_DEPLOY_APPLICATION_TO_DEVELOPM_SKILLS
from app.workflows.auto_deploy_application_to_developm_workflow import create_auto_deploy_application_to_developm_workflow
load_dotenv()
# ── Logging ──────────────────────────────────────────────────────────────
logging.basicConfig(
level=getattr(logging, LOG_LEVEL.upper(), logging.INFO),
format="%(asctime)s %(name)s %(levelname)s %(message)s",
)
logger = logging.getLogger(__name__)
# ── LLM ──────────────────────────────────────────────────────────────────
rate_limiter = InMemoryRateLimiter(
requests_per_second=10 / 60,
check_every_n_seconds=0.1,
max_bucket_size=10,
)
llm = AzureChatOpenAI(
temperature=0,
azure_deployment=AZURE_OPENAI_DEPLOYMENT,
api_version=AZURE_OPENAI_API_VERSION,
azure_endpoint=AZURE_OPENAI_ENDPOINT or "",
api_key=AZURE_OPENAI_API_KEY or "",
max_retries=5,
timeout=120,
rate_limiter=rate_limiter,
)
workflow = create_auto_deploy_application_to_developm_workflow(llm)
# ── Endpoints ────────────────────────────────────────────────────────────
async def health_check(request: Request) -> JSONResponse:
return JSONResponse({"status": "healthy", "agent": 'AutoDeployApplicationToDevelopm'})
async def agent_manifest(request: Request) -> JSONResponse:
"""GET /.well-known/agent.json"""
return JSONResponse({
"name": AGENT_CONFIG["name"],
"version": AGENT_CONFIG["version"],
"description": AGENT_CONFIG["description"],
"url": "/",
"skills": [
{
"id": s.id,
"name": s.name,
"description": s.description,
"tags": s.tags,
"inputSchema": AGENT_CONFIG.get("input_schema"),
"outputSchema": AGENT_CONFIG.get("output_schema"),
} for s in AUTO_DEPLOY_APPLICATION_TO_DEVELOPM_SKILLS
],
"capabilities": AGENT_CONFIG["capabilities"],
})
async def process_endpoint(request: Request) -> JSONResponse:
"""POST /process — run the agent workflow."""
try:
body = await request.json()
result = await workflow.ainvoke(body)
return JSONResponse(result)
except Exception as exc:
logger.error("Processing failed: %s", exc, exc_info=True)
return JSONResponse({"error": str(exc)}, status_code=500)
# ── Self-registration ────────────────────────────────────────────────────
async def _register_with_registry():
if not REGISTRY_URL:
logger.info("REGISTRY_URL not set — skipping self-registration")
return
await asyncio.sleep(2)
url = f"{REGISTRY_URL.rstrip('/')}/agents/register-url"
for attempt in range(3):
try:
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(url, json={"endpoint": AGENT_SELF_URL})
if resp.status_code in (200, 201):
logger.info("Self-registered with agent-registry at %s", REGISTRY_URL)
return
logger.warning("Registration attempt %d: HTTP %d", attempt + 1, resp.status_code)
except Exception as exc:
logger.warning("Registration attempt %d failed: %s", attempt + 1, exc)
await asyncio.sleep(5)
logger.error("Failed to self-register after 3 attempts")
@asynccontextmanager
async def lifespan(app):
task = asyncio.create_task(_register_with_registry())
yield
task.cancel()
app = Starlette(
routes=[
Route("/health", methods=["GET"], endpoint=health_check),
Route("/.well-known/agent.json", methods=["GET"], endpoint=agent_manifest),
Route("/process", methods=["POST"], endpoint=process_endpoint),
],
lifespan=lifespan,
)
@click.command()
@click.option("--host", default="0.0.0.0")
@click.option("--port", default=8080, type=int)
def main(host: str, port: int):
uvicorn.run(app, host=host, port=port, log_level=LOG_LEVEL.lower())
if __name__ == "__main__":
main()

21
app/config.py Normal file
View File

@@ -0,0 +1,21 @@
"""
Configuration for the AutoDeployApplicationToDevelopm agent.
"""
import os
# ── Azure OpenAI ─────────────────────────────────────────────────────────
AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
AZURE_OPENAI_API_VERSION = os.getenv("AZURE_OPENAI_API_VERSION", "2024-08-01-preview")
AZURE_OPENAI_DEPLOYMENT = os.getenv("AZURE_OPENAI_DEPLOYMENT", "gpt-4o")
# ── Agent Registry ───────────────────────────────────────────────────────
REGISTRY_URL = os.getenv(
"REGISTRY_URL", "http://agent-gateway.agents.svc.cluster.local"
)
AGENT_SELF_URL = os.getenv(
"AGENT_SELF_URL", "http://auto-deploy-application-to-developm.agents.svc.cluster.local"
)
# ── Logging ──────────────────────────────────────────────────────────────
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")

0
app/nodes/__init__.py Normal file
View File

43
app/nodes/core_node.py Normal file
View File

@@ -0,0 +1,43 @@
async def process(state: dict) -> dict:
"""
Deploys an application to the development environment using the provided CI pipeline configuration
and PostgreSQL version. Utilizes an LLM to generate deployment instructions and returns the deployment
status and URL.
Args:
state (dict): A dictionary containing the input fields:
- ci_pipeline_config_path (str): Path to the CI pipeline configuration file.
- postgres_version (str): Version of PostgreSQL to use.
Returns:
dict: A dictionary containing the output fields:
- deployment_status (str): Status of the deployment (e.g., "success", "failure").
- deployment_url (str): URL of the deployed application.
"""
from app.agent import llm
from langchain_core.messages import SystemMessage, HumanMessage
try:
ci_pipeline_config_path = state.get("ci_pipeline_config_path", "")
postgres_version = state.get("postgres_version", "")
if not ci_pipeline_config_path or not postgres_version:
raise ValueError("Both 'ci_pipeline_config_path' and 'postgres_version' must be provided.")
messages = [
SystemMessage(content="You are a deployment assistant. Your task is to deploy applications to a development environment."),
HumanMessage(content=f"Deploy the application using the CI pipeline configuration at '{ci_pipeline_config_path}' and PostgreSQL version '{postgres_version}'. Provide the deployment status and URL.")
]
response = await llm.ainvoke(messages)
deployment_details = response.content.strip().split("\n")
if len(deployment_details) < 2:
raise ValueError("Unexpected response format from LLM.")
deployment_status = deployment_details[0].strip()
deployment_url = deployment_details[1].strip()
return {"deployment_status": deployment_status, "deployment_url": deployment_url, "phase": "complete"}
except Exception as exc:
return {"error": str(exc), "phase": "failed"}

29
app/skills.py Normal file
View File

@@ -0,0 +1,29 @@
"""
A2A skill declarations for AutoDeployApplicationToDevelopm.
"""
from a2a.types import AgentSkill
AUTO_DEPLOY_APPLICATION_TO_DEVELOPM_SKILLS = [
AgentSkill(
id="auto_deploy_application_to_developm_skill",
name="AutoDeployApplicationToDevelopm",
description="Deploy application to development environment",
tags=["auto-generated"],
examples=[],
),
]
AGENT_CONFIG = {
"name": "AutoDeployApplicationToDevelopm",
"description": "Deploy claims-api to development environment with Postgres database.",
"version": "1.0.0",
"framework": "LangGraph + Starlette",
"capabilities": {
"streaming": False,
"async": True,
},
"input_schema": {"ci_pipeline_config_path": "string", "postgres_version": "string"},
"output_schema": {"deployment_status": "string", "deployment_url": "string"},
}

0
app/states/__init__.py Normal file
View File

View File

@@ -0,0 +1,17 @@
"""
State definitions for AutoDeployApplicationToDevelopm agent.
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from langgraph.graph import MessagesState
class AutoDeployApplicationToDevelopmState(MessagesState):
"""Workflow state for AutoDeployApplicationToDevelopm."""
# ── Input fields ─────────────────────────────────────────────────────
pass
# ── Output fields ────────────────────────────────────────────────────
pass
# ── Internal ─────────────────────────────────────────────────────────
error: Optional[str]
phase: str

View File

View File

@@ -0,0 +1,19 @@
"""
LangGraph workflow for AutoDeployApplicationToDevelopm agent.
"""
from langgraph.graph import StateGraph, END
from app.states.auto_deploy_application_to_developm_state import AutoDeployApplicationToDevelopmState
from app.nodes.core_node import process
def create_auto_deploy_application_to_developm_workflow(llm):
"""Build and compile the AutoDeployApplicationToDevelopm workflow graph."""
graph = StateGraph(AutoDeployApplicationToDevelopmState)
graph.add_node("process", process)
graph.set_entry_point("process")
graph.add_edge("process", END)
return graph.compile()

35
catalog-info.yaml Normal file
View File

@@ -0,0 +1,35 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: auto-deploy-application-to-developm
description: "Deploy claims-api to development environment with Postgres database."
annotations:
backstage.io/kubernetes-label-selector: app=auto-deploy-application-to-developm
backstage.io/kubernetes-namespace: agents
backstage.io/techdocs-ref: dir:.
gitea.kyndemo.live/repo-slug: generated-agents/auto-deploy-application-to-developm
grafana/grafana-instance: default
grafana/alert-label-selector: app=auto-deploy-application-to-developm
grafana/dashboard-selector: uid == 'otel-app-observability-v2'
grafana.com/dashboard-url: https://grafana.kyndemo.live/d/otel-app-observability-v2/opentelemetry-application-observability?orgId=1&var-app=auto-deploy-application-to-developm
tags:
- agent
- a2a
- auto-generated
links:
- icon: github
title: Source Repository
url: https://gitea.kyndemo.live/generated-agents/auto-deploy-application-to-developm
- icon: code
title: CI/CD Pipelines
url: https://gitea.kyndemo.live/generated-agents/auto-deploy-application-to-developm/actions
- icon: dashboard
title: Grafana Dashboard
url: https://grafana.kyndemo.live/d/otel-app-observability-v2/opentelemetry-application-observability?orgId=1&var-app=auto-deploy-application-to-developm
spec:
type: service
lifecycle: experimental
owner: group:default/agentic-agents
system: agentic-agents
dependsOn:
- resource:default/cjot-aks

13
k8s/configmap.yaml Normal file
View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: auto-deploy-application-to-developm-config
namespace: agents
data:
REGISTRY_URL: "http://agent-gateway.agents.svc.cluster.local"
AGENT_SELF_URL: "http://auto-deploy-application-to-developm.agents.svc.cluster.local"
AZURE_OPENAI_DEPLOYMENT: "gpt-4o"
AZURE_OPENAI_API_VERSION: "2024-08-01-preview"
LOG_LEVEL: "INFO"
OTEL_SERVICE_NAME: "auto-deploy-application-to-developm"
OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector.monitoring.svc.cluster.local:4318"

81
k8s/deployment.yaml Normal file
View File

@@ -0,0 +1,81 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: auto-deploy-application-to-developm
namespace: agents
labels:
app: auto-deploy-application-to-developm
component: agent
generated-by: agent-factory
spec:
replicas: 1
selector:
matchLabels:
app: auto-deploy-application-to-developm
template:
metadata:
labels:
app: auto-deploy-application-to-developm
azure.workload.identity/use: "true"
annotations:
instrumentation.opentelemetry.io/inject-python: "monitoring/otel-instrumentation"
spec:
serviceAccountName: agents-sa
containers:
- name: auto-deploy-application-to-developm
image: bstagecjotdevacr.azurecr.io/auto-deploy-application-to-developm:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
protocol: TCP
envFrom:
- configMapRef:
name: auto-deploy-application-to-developm-config
env:
- name: AZURE_OPENAI_ENDPOINT
valueFrom:
secretKeyRef:
name: agents-kv-sync
key: azure-openai-endpoint
- name: AZURE_OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: agents-kv-sync
key: azure-openai-api-key
- name: OTEL_SERVICE_NAME
value: "auto-deploy-application-to-developm"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.namespace=agents,deployment.environment=production"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 5
volumeMounts:
- name: secrets-store
mountPath: "/mnt/secrets-store"
readOnly: true
volumes:
- name: secrets-store
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: agents-kv-spc

16
k8s/service.yaml Normal file
View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: auto-deploy-application-to-developm
namespace: agents
labels:
app: auto-deploy-application-to-developm
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: auto-deploy-application-to-developm

18
requirements.txt Normal file
View File

@@ -0,0 +1,18 @@
# Auto-generated by Agent Factory
uvicorn[standard]==0.32.1
starlette>=0.28.0
pydantic>=2.11.3
python-dotenv==1.2.2
httpx>=0.28.1
click>=8.1.8
# A2A Protocol
a2a-sdk[http-server]>=0.3.0
# LangChain & LangGraph
langchain>=1.2.10
langchain-openai>=0.2.12
langchain-core>=1.2.11
langgraph>=0.2.59
langgraph-checkpoint>=2.0.6
openai>=1.109.1

54
scripts/deploy.sh Normal file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
# deploy.sh — Build auto-deploy-application-to-developm image in ACR and deploy to AKS
set -e
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ACR_NAME="bstagecjotdevacr"
APP_NAME="auto-deploy-application-to-developm"
NAMESPACE="agents"
VERSION_FILE="${SCRIPT_DIR}/../.image-version"
TAG=""
while [[ "$#" -gt 0 ]]; do
case $1 in
--tag) TAG="$2"; shift ;;
esac
shift
done
if [ -z "$TAG" ]; then
if [ -f "$VERSION_FILE" ]; then
CURRENT_VERSION=$(cat "$VERSION_FILE")
else
CURRENT_VERSION="1.0.0"
fi
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
PATCH=$((PATCH + 1))
TAG="${MAJOR}.${MINOR}.${PATCH}"
echo "$TAG" > "$VERSION_FILE"
fi
IMAGE="${ACR_NAME}.azurecr.io/${APP_NAME}:${TAG}"
echo "======================================================================"
echo " auto-deploy-application-to-developm — Build & Deploy"
echo " Image: ${IMAGE}"
echo "======================================================================"
az acr build --registry ${ACR_NAME} \
--image ${APP_NAME}:${TAG} \
--image ${APP_NAME}:latest \
"${SCRIPT_DIR}/.."
kubectl apply -f "${SCRIPT_DIR}/../k8s/configmap.yaml" 2>/dev/null || true
kubectl apply -f "${SCRIPT_DIR}/../k8s/deployment.yaml"
kubectl apply -f "${SCRIPT_DIR}/../k8s/service.yaml" 2>/dev/null || true
kubectl set image deployment/${APP_NAME} \
${APP_NAME}=${IMAGE} \
-n ${NAMESPACE}
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=120s
echo "✓ Done. Image: ${IMAGE}"
echo " Port forward: kubectl port-forward -n ${NAMESPACE} deployment/${APP_NAME} 8080:8080"

66
scripts/full-deploy.sh Normal file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
# full-deploy.sh — Preflight + build + deploy auto-deploy-application-to-developm to AKS
set -e
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ACR_NAME="bstagecjotdevacr"
APP_NAME="auto-deploy-application-to-developm"
NAMESPACE="agents"
VERSION_FILE="${SCRIPT_DIR}/../.image-version"
TAG=""
while [[ "$#" -gt 0 ]]; do
case $1 in
--tag) TAG="$2"; shift ;;
esac
shift
done
print_success() { echo -e "${GREEN}$1${NC}"; }
print_error() { echo -e "${RED}$1${NC}"; exit 1; }
command -v kubectl &>/dev/null || print_error "kubectl not found."
command -v az &>/dev/null || print_error "Azure CLI not found."
az account show &>/dev/null || print_error "Not logged in to Azure."
kubectl cluster-info &>/dev/null || print_error "Cannot connect to cluster."
CLUSTER=$(kubectl config current-context)
print_success "Connected to cluster: ${CLUSTER}"
if [ -z "$TAG" ]; then
if [ -f "$VERSION_FILE" ]; then
CURRENT_VERSION=$(cat "$VERSION_FILE")
else
CURRENT_VERSION="1.0.0"
fi
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
PATCH=$((PATCH + 1))
TAG="${MAJOR}.${MINOR}.${PATCH}"
echo "$TAG" > "$VERSION_FILE"
fi
IMAGE="${ACR_NAME}.azurecr.io/${APP_NAME}:${TAG}"
print_success "Image tag: ${TAG}"
az acr build --registry ${ACR_NAME} \
--image ${APP_NAME}:${TAG} \
--image ${APP_NAME}:latest \
"${SCRIPT_DIR}/.." || print_error "ACR build failed."
print_success "Image built: ${IMAGE}"
kubectl apply -f "${SCRIPT_DIR}/../k8s/configmap.yaml" 2>/dev/null || true
kubectl apply -f "${SCRIPT_DIR}/../k8s/deployment.yaml" || print_error "Failed to apply deployment."
kubectl apply -f "${SCRIPT_DIR}/../k8s/service.yaml" || print_error "Failed to apply service."
print_success "Manifests applied"
kubectl set image deployment/${APP_NAME} ${APP_NAME}=${IMAGE} -n ${NAMESPACE}
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s || \
print_error "Rollout failed."
print_success "Rollout complete"
echo ""
echo -e "${GREEN} auto-deploy-application-to-developm deployed: ${IMAGE}${NC}"
echo " Port forward: kubectl port-forward -n ${NAMESPACE} deployment/${APP_NAME} 8080:8080"