agent-factory: generate agent auto-retrieve-files-from-a-reposito

This commit is contained in:
2026-04-13 20:00:51 +00:00
parent 56916f44cf
commit 940e2a5b80
19 changed files with 569 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 @@
"""
AutoRetrieveFilesFromAReposito 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_RETRIEVE_FILES_FROM_A_REPOSITO_SKILLS
from app.workflows.auto_retrieve_files_from_a_reposito_workflow import create_auto_retrieve_files_from_a_reposito_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_retrieve_files_from_a_reposito_workflow(llm)
# ── Endpoints ────────────────────────────────────────────────────────────
async def health_check(request: Request) -> JSONResponse:
return JSONResponse({"status": "healthy", "agent": 'AutoRetrieveFilesFromAReposito'})
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_RETRIEVE_FILES_FROM_A_REPOSITO_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 AutoRetrieveFilesFromAReposito 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-retrieve-files-from-a-reposito.agents.svc.cluster.local"
)
# ── Logging ──────────────────────────────────────────────────────────────
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")

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

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

@@ -0,0 +1,26 @@
async def process(state: dict) -> dict:
"""
This node retrieves files from a repository based on the provided URL and update scope.
It uses an LLM to analyze and extract relevant workflow files from the repository.
"""
from app.agent import llm
from langchain_core.messages import SystemMessage, HumanMessage
try:
repository_url = state.get("repository_url", "")
update_scope = state.get("update_scope", "")
if not repository_url or not update_scope:
raise ValueError("Both 'repository_url' and 'update_scope' must be provided.")
messages = [
SystemMessage(content="You are an assistant that retrieves workflow files from a repository."),
HumanMessage(content=f"Repository URL: {repository_url}\nUpdate Scope: {update_scope}")
]
response = await llm.ainvoke(messages)
workflow_files = response.content.splitlines() # Assuming the LLM returns file names as newline-separated strings.
return {"workflow_files": workflow_files, "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 AutoRetrieveFilesFromAReposito.
"""
from a2a.types import AgentSkill
AUTO_RETRIEVE_FILES_FROM_A_REPOSITO_SKILLS = [
AgentSkill(
id="auto_retrieve_files_from_a_reposito_skill",
name="AutoRetrieveFilesFromAReposito",
description="Retrieve files from a repository",
tags=["auto-generated"],
examples=[],
),
]
AGENT_CONFIG = {
"name": "AutoRetrieveFilesFromAReposito",
"description": "Fetch all Java workflow files from the Gitea repository",
"version": "1.0.0",
"framework": "LangGraph + Starlette",
"capabilities": {
"streaming": False,
"async": True,
},
"input_schema": {"repository_url": "string", "update_scope": "string"},
"output_schema": {"workflow_files": "string[]"},
}

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

View File

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

View File

View File

@@ -0,0 +1,19 @@
"""
LangGraph workflow for AutoRetrieveFilesFromAReposito agent.
"""
from langgraph.graph import StateGraph, END
from app.states.auto_retrieve_files_from_a_reposito_state import AutoRetrieveFilesFromARepositoState
from app.nodes.core_node import process
def create_auto_retrieve_files_from_a_reposito_workflow(llm):
"""Build and compile the AutoRetrieveFilesFromAReposito workflow graph."""
graph = StateGraph(AutoRetrieveFilesFromARepositoState)
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-retrieve-files-from-a-reposito
description: "Fetch all Java workflow files from the Gitea repository"
annotations:
backstage.io/kubernetes-label-selector: app=auto-retrieve-files-from-a-reposito
backstage.io/kubernetes-namespace: agents
backstage.io/techdocs-ref: dir:.
gitea.kyndemo.live/repo-slug: generated-agents/auto-retrieve-files-from-a-reposito
grafana/grafana-instance: default
grafana/alert-label-selector: app=auto-retrieve-files-from-a-reposito
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-retrieve-files-from-a-reposito
tags:
- agent
- a2a
- auto-generated
links:
- icon: github
title: Source Repository
url: https://gitea.kyndemo.live/generated-agents/auto-retrieve-files-from-a-reposito
- icon: code
title: CI/CD Pipelines
url: https://gitea.kyndemo.live/generated-agents/auto-retrieve-files-from-a-reposito/actions
- icon: dashboard
title: Grafana Dashboard
url: https://grafana.kyndemo.live/d/otel-app-observability-v2/opentelemetry-application-observability?orgId=1&var-app=auto-retrieve-files-from-a-reposito
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-retrieve-files-from-a-reposito-config
namespace: agents
data:
REGISTRY_URL: "http://agent-gateway.agents.svc.cluster.local"
AGENT_SELF_URL: "http://auto-retrieve-files-from-a-reposito.agents.svc.cluster.local"
AZURE_OPENAI_DEPLOYMENT: "gpt-4o"
AZURE_OPENAI_API_VERSION: "2024-08-01-preview"
LOG_LEVEL: "INFO"
OTEL_SERVICE_NAME: "auto-retrieve-files-from-a-reposito"
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-retrieve-files-from-a-reposito
namespace: agents
labels:
app: auto-retrieve-files-from-a-reposito
component: agent
generated-by: agent-factory
spec:
replicas: 1
selector:
matchLabels:
app: auto-retrieve-files-from-a-reposito
template:
metadata:
labels:
app: auto-retrieve-files-from-a-reposito
azure.workload.identity/use: "true"
annotations:
instrumentation.opentelemetry.io/inject-python: "monitoring/otel-instrumentation"
spec:
serviceAccountName: agents-sa
containers:
- name: auto-retrieve-files-from-a-reposito
image: bstagecjotdevacr.azurecr.io/auto-retrieve-files-from-a-reposito:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
protocol: TCP
envFrom:
- configMapRef:
name: auto-retrieve-files-from-a-reposito-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-retrieve-files-from-a-reposito"
- 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-retrieve-files-from-a-reposito
namespace: agents
labels:
app: auto-retrieve-files-from-a-reposito
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: auto-retrieve-files-from-a-reposito

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.0.1
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-retrieve-files-from-a-reposito image in ACR and deploy to AKS
set -e
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ACR_NAME="bstagecjotdevacr"
APP_NAME="auto-retrieve-files-from-a-reposito"
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-retrieve-files-from-a-reposito — 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-retrieve-files-from-a-reposito 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-retrieve-files-from-a-reposito"
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-retrieve-files-from-a-reposito deployed: ${IMAGE}${NC}"
echo " Port forward: kubectl port-forward -n ${NAMESPACE} deployment/${APP_NAME} 8080:8080"