Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions exgentic_a2a_runner/deploy-agent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ KEYCLOAK_PASSWORD="unknown"
BENCHMARK_NAME=""
AGENT_NAME_INPUT=""
USE_MCP_GATEWAY="false"
# IBAC_ENABLED lets .env pre-set the default; --ibac / --no-ibac always override.
USE_IBAC="${IBAC_ENABLED:-false}"

# Parse arguments
while [[ $# -gt 0 ]]; do
Expand Down Expand Up @@ -41,6 +43,14 @@ while [[ $# -gt 0 ]]; do
USE_MCP_GATEWAY="true"
shift
;;
--ibac)
USE_IBAC="true"
shift
;;
--no-ibac)
USE_IBAC="false"
shift
;;
-h|--help)
echo "Usage: $0 --benchmark <name> --agent <name> [OPTIONS]"
echo ""
Expand All @@ -53,8 +63,17 @@ while [[ $# -gt 0 ]]; do
echo " --keycloak-user USER Keycloak username (default: admin)"
echo " --keycloak-pass PASS Keycloak password (auto-detected from cluster if not provided)"
echo " --use-mcp-gateway Connect agent to MCP Gateway instead of direct MCP server"
echo " --ibac Enable IBAC by patching the authbridge sidecar's plugin pipeline"
echo " --no-ibac Deploy without IBAC (default; overrides IBAC_ENABLED env)"
echo " -h, --help Show this help message"
echo ""
echo "IBAC Environment Variables (used with --ibac):"
echo " IBAC_ENABLED Default for --ibac flag if set to true"
echo " IBAC_JUDGE_ENDPOINT Judge LLM base URL (default: http://host.docker.internal:11434)"
echo " IBAC_JUDGE_MODEL Judge model id (default: llama3.2:3b)"
echo " IBAC_AGENT_LLM_HOST Hostname of the agent's own LLM (auto-derived from OPENAI_API_BASE)"
echo " IBAC_TIMEOUT_MS Per-judge-call timeout in ms (default: 15000)"
echo ""
echo "Examples:"
echo " $0 --benchmark gsm8k --agent generic_agent"
echo " $0 --benchmark tau2 --agent tool_calling --model Azure/gpt-4o-mini"
Expand Down Expand Up @@ -462,9 +481,12 @@ if [ "$AGENT_NAME_INPUT" = "tool_calling" ]; then
ENV_VARS_WITH_CONFIG=$(echo "$ENV_VARS_WITH_CONFIG" | jq ". + [{\"name\": \"EXGENTIC_SET_AGENT_ENABLE_TOOL_SHORTLISTING\", \"value\": \"true\"}]")
fi

# Add EXGENTIC_OTEL_ENABLED and OTEL_EXPORTER_OTLP_PROTOCOL to environment variables
echo "Adding EXGENTIC_OTEL_ENABLED and OTEL_EXPORTER_OTLP_PROTOCOL to environment variables"
ENV_VARS_WITH_CONFIG=$(echo "$ENV_VARS_WITH_CONFIG" | jq ". + [{\"name\": \"EXGENTIC_OTEL_ENABLED\", \"value\": \"true\"}, {\"name\": \"OTEL_EXPORTER_OTLP_PROTOCOL\", \"value\": \"http/protobuf\"}]")
# Add EXGENTIC_OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT, and OTEL_EXPORTER_OTLP_PROTOCOL.
# The kagenti-deps otel-collector binds OTLP/HTTP on 8335 (and gRPC on 4317);
# nothing is listening on 4318, so requests there return 503 and crash the
# agent's strict OTEL startup probe.
echo "Adding EXGENTIC_OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL"
ENV_VARS_WITH_CONFIG=$(echo "$ENV_VARS_WITH_CONFIG" | jq ". + [{\"name\": \"EXGENTIC_OTEL_ENABLED\", \"value\": \"true\"}, {\"name\": \"OTEL_EXPORTER_OTLP_ENDPOINT\", \"value\": \"http://otel-collector.kagenti-system.svc.cluster.local:8335\"}, {\"name\": \"OTEL_EXPORTER_OTLP_PROTOCOL\", \"value\": \"http/protobuf\"}]")

# Set agent runner to thread for in-process execution (avoids venv subprocess overhead)
echo "Adding EXGENTIC_DEFAULT_RUNNER=thread for agent"
Expand All @@ -476,6 +498,14 @@ echo ""
# Step 8: Deploy agent via Kagenti API
echo "Step 8: Deploying agent via Kagenti API..."

# IBAC patches the operator-injected authbridge sidecar's plugin pipeline, so the
# operator must inject one in the first place. Without authBridgeEnabled, no
# authbridge-config-<agent> ConfigMap is created and apply-ibac.sh has nothing to patch.
AUTHBRIDGE_ENABLED="false"
if [ "$USE_IBAC" = "true" ]; then
AUTHBRIDGE_ENABLED="true"
fi

if [ "$DEPLOYMENT_TYPE" = "source" ]; then
# Deploy generic agent from source
AGENT_JSON=$(cat <<EOF
Expand All @@ -500,7 +530,7 @@ if [ "$DEPLOYMENT_TYPE" = "source" ]; then
}
],
"createHttpRoute": false,
"authBridgeEnabled": false,
"authBridgeEnabled": $AUTHBRIDGE_ENABLED,
"spireEnabled": false
}
EOF
Expand Down Expand Up @@ -530,7 +560,7 @@ else
}
],
"createHttpRoute": false,
"authBridgeEnabled": false,
"authBridgeEnabled": $AUTHBRIDGE_ENABLED,
"spireEnabled": false
}
EOF
Expand Down Expand Up @@ -687,6 +717,24 @@ kubectl rollout status deployment/$AGENT_NAME -n $NAMESPACE --timeout=120s
echo "✓ Deployment stable"
echo ""

# Step 11.4: Optionally inject IBAC sidecar overlay
if [ "$USE_IBAC" = "true" ]; then
echo "Step 11.4: Applying IBAC overlay..."
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ ! -x "$SCRIPT_DIR/ibac/apply-ibac.sh" ]; then
echo "Error: $SCRIPT_DIR/ibac/apply-ibac.sh not found or not executable"
exit 1
fi
AGENT_NAME="$AGENT_NAME" NAMESPACE="$NAMESPACE" \
OPENAI_API_BASE="${OPENAI_API_BASE:-}" \
IBAC_JUDGE_ENDPOINT="${IBAC_JUDGE_ENDPOINT:-}" \
IBAC_JUDGE_MODEL="${IBAC_JUDGE_MODEL:-}" \
IBAC_AGENT_LLM_HOST="${IBAC_AGENT_LLM_HOST:-}" \
IBAC_TIMEOUT_MS="${IBAC_TIMEOUT_MS:-}" \
"$SCRIPT_DIR/ibac/apply-ibac.sh"
echo ""
fi

# Step 12: Test agent card access
echo "Step 12: Testing agent card access..."

Expand Down Expand Up @@ -760,6 +808,7 @@ echo " Tool: $TOOL_NAME.$NAMESPACE:8000"
echo " Model: $MODEL_NAME"
echo " CPU Limit: 4 cores"
echo " Memory Limit: 3Gi"
echo " IBAC: $USE_IBAC"
if [ -n "$OPENAI_API_BASE" ]; then
echo " LLM_API_BASE: $OPENAI_API_BASE"
echo " OPENAI_API_BASE: $OPENAI_API_BASE"
Expand Down
22 changes: 21 additions & 1 deletion exgentic_a2a_runner/deploy-and-evaluate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ KEYCLOAK_USERNAME="admin"
KEYCLOAK_PASSWORD="unknown"
MLFLOW_ENABLED="false"
USE_MCP_GATEWAY="${USE_MCP_GATEWAY:-false}"
USE_IBAC="${IBAC_ENABLED:-false}"

# Parse arguments
while [[ $# -gt 0 ]]; do
Expand Down Expand Up @@ -59,6 +60,14 @@ while [[ $# -gt 0 ]]; do
USE_MCP_GATEWAY="true"
shift
;;
--ibac)
USE_IBAC="true"
shift
;;
--no-ibac)
USE_IBAC="false"
shift
;;
-h|--help)
echo "Usage: $0 --benchmark <name> --agent <name> [OPTIONS]"
echo ""
Expand All @@ -73,6 +82,8 @@ while [[ $# -gt 0 ]]; do
echo " --keycloak-pass PASS Keycloak password (default: admin)"
echo " --mlflow Enable MLflow tracing via OTEL collector during evaluation"
echo " --use-mcp-gateway Route MCP traffic through the MCP Gateway"
echo " --ibac Enable IBAC by patching the authbridge sidecar's plugin pipeline"
echo " --no-ibac Deploy without IBAC (default; overrides IBAC_ENABLED env)"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
Expand All @@ -90,6 +101,7 @@ while [[ $# -gt 0 ]]; do
echo ""
echo "Environment Variables:"
echo " USE_MCP_GATEWAY=true Same as --use-mcp-gateway (set in .env)"
echo " IBAC_ENABLED=true Same as --ibac (set in .env)"
exit 0
;;
-*)
Expand Down Expand Up @@ -127,6 +139,7 @@ echo "Model: $MODEL_NAME"
echo "Keycloak User: $KEYCLOAK_USERNAME"
echo "MLflow tracing: $MLFLOW_ENABLED"
echo "MCP Gateway: $USE_MCP_GATEWAY"
echo "IBAC: $USE_IBAC"
echo ""

# Build gateway flag for sub-scripts
Expand All @@ -135,6 +148,12 @@ if [ "$USE_MCP_GATEWAY" = "true" ]; then
MCP_GATEWAY_FLAG="--use-mcp-gateway"
fi

# Build IBAC flag for deploy-agent.sh (explicit --ibac/--no-ibac so CLI always wins over agent script's own IBAC_ENABLED read)
IBAC_FLAG="--no-ibac"
if [ "$USE_IBAC" = "true" ]; then
IBAC_FLAG="--ibac"
fi

# Step 1: Deploy benchmark
echo "=========================================="
echo "Step 1/3: Deploying Benchmark"
Expand Down Expand Up @@ -162,7 +181,8 @@ echo "=========================================="
--model "$MODEL_NAME" \
--keycloak-user "$KEYCLOAK_USERNAME" \
--keycloak-pass "$KEYCLOAK_PASSWORD" \
$MCP_GATEWAY_FLAG
$MCP_GATEWAY_FLAG \
$IBAC_FLAG

if [ $? -ne 0 ]; then
echo "Error: Agent deployment failed"
Expand Down
31 changes: 31 additions & 0 deletions exgentic_a2a_runner/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,37 @@ USE_MCP_GATEWAY=false
# so "list_tasks" becomes "exgentic_list_tasks". Set this to match the gateway config.
# EXGENTIC_MCP_TOOL_PREFIX=exgentic_

# ============================================================
# IBAC CONFIGURATION (Optional)
# ============================================================
# IBAC (Intent-Based Access Control) is enabled by patching the
# operator-injected authbridge sidecar's plugin pipeline to add the
# `ibac` plugin (and its a2a-parser / mcp-parser / inference-parser
# dependencies). The judge LLM compares each outbound call against the
# user's most recent A2A intent and denies misaligned actions.
# Used by deploy-agent.sh when --ibac is passed (or IBAC_ENABLED=true).
#
# Prerequisite: the cluster's authbridge-proxy image must include the
# `ibac` plugin. Older chart pins won't have it.

# Default for the --ibac flag. --ibac / --no-ibac on the command line always wins.
IBAC_ENABLED=false

# Judge LLM base URL. Must be OpenAI-compatible (POST /v1/chat/completions).
# Default targets ollama on the host.
# IBAC_JUDGE_ENDPOINT=http://host.docker.internal:11434

# Judge model id served by the endpoint above.
# IBAC_JUDGE_MODEL=llama3.2:3b

# Hostname of the agent's own LLM endpoint. Added to the IBAC bypass list so
# the agent's reasoning calls aren't recursively judged. Auto-derived from
# OPENAI_API_BASE if unset; defaults to host.docker.internal otherwise.
# IBAC_AGENT_LLM_HOST=host.docker.internal

# Per-judge-call timeout in milliseconds. Bump if the judge model is slow.
# IBAC_TIMEOUT_MS=15000

# ============================================================
# DEBUG CONFIGURATION (Optional)
# ============================================================
Expand Down
11 changes: 10 additions & 1 deletion exgentic_a2a_runner/exgentic_a2a_runner/a2a_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def send_prompt(

async def _async_send_prompt(self, prompt: str, timeout_s: float, otel_context=None, session_id: Optional[str] = None) -> str:
"""Async implementation using the standard a2a-sdk."""
import uuid

import httpx
from a2a.client import ClientConfig, ClientFactory, create_text_message_object
from a2a.client.card_resolver import A2ACardResolver
Expand All @@ -90,7 +92,14 @@ async def _async_send_prompt(self, prompt: str, timeout_s: float, otel_context=N
else:
token = None

httpx_client = httpx.AsyncClient(timeout=timeout_s)
# x-session-id is a per-prompt correlation id. The current IBAC plugin
# keys intent off authbridge's own session store (populated by a2a-parser),
# not this header — but emitting it is harmless and useful for tracing.
session_id = uuid.uuid4().hex
httpx_client = httpx.AsyncClient(
timeout=timeout_s,
headers={"x-session-id": session_id},
)
if self.otel_enabled:
try:
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
Expand Down
Loading
Loading