Skip to content

Commit a2599ee

Browse files
Merge pull request #35 from tarun-etikala/pr/helm-llamaindex-websearch
feat: llamaindex agent migration to Helm chart, Makefile workflow
2 parents 0ed77c4 + 38c41e7 commit a2599ee

31 files changed

Lines changed: 974 additions & 420 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
.env*
22
!.env.example
3-
.env
43
.vscode
54
.idea
65
.DS_Store
@@ -14,6 +13,7 @@ config.toml
1413
milvus_data/
1514
*.egg-info/
1615
uv.lock
16+
.helm-secrets.yaml
1717
agents/langflow/simple_tool_calling_agent/local/.ollama-enabled
1818
*.db
1919
CLAUDE.md

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ agentic-starter-kits/
6262
│ │ └── mcp_agent/ # AutoGen + MCP (SSE)
6363
│ └── langflow/
6464
│ └── simple_tool_calling_agent/ # Langflow tool-calling agent
65+
├── charts/
66+
│ └── agent/ # Shared Helm chart for all agents
67+
├── docs/ # Guides: local dev, deployment, contributing
6568
└── README.md
6669
```
6770

@@ -101,6 +104,12 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
101104

102105
---
103106

107+
## Documentation
108+
109+
- [Local Development](./docs/local-development.md) — Ollama + Llama Stack setup
110+
- [OpenShift Deployment](./docs/openshift-deployment.md) — Helm-based deployment guide
111+
- [Adding a New Agent](./docs/adding-a-new-agent.md) — How to contribute a new agent template
112+
104113
## Additional Resources
105114

106115
- **Llama Stack Documentation**: https://llama-stack.readthedocs.io/
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.env
2+
.env.*
3+
!.env.example
4+
.venv
5+
__pycache__
6+
*.pyc
7+
*.pyo
8+
.pytest_cache
9+
.mypy_cache
10+
.ruff_cache
11+
.git
12+
.gitignore
13+
.helm-secrets.yaml
14+
.DS_Store
15+
tests/
16+
examples/
17+
docs/
18+
*.md
Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1-
## LOCAL
2-
# MODEL_ID=
3-
# BASE_URL=
4-
# API_KEY=<your-api-key> # Set to "no-key" if your LLM provider does not require an API key
5-
# CONTAINER_IMAGE=
1+
# Required
2+
API_KEY=
3+
BASE_URL=
4+
MODEL_ID=
65

7-
## LOCAL TRACING
6+
# Deployment
7+
CONTAINER_IMAGE=
88

9+
# Local Tracing (optional)
910
# MLFLOW_TRACKING_URI=
1011
# MLFLOW_EXPERIMENT_NAME=
1112
# MLFLOW_HEALTH_CHECK_TIMEOUT= # (default is 5s)
1213
# MLFLOW_HTTP_REQUEST_TIMEOUT= # (default is 120s)
1314
# MLFLOW_HTTP_REQUEST_MAX_RETRIES=
1415

15-
## OPENSHIFT CLUSTER TRACING
16-
17-
# Openshift Cluster
16+
# OpenShift Cluster Tracing (optional)
1817
# MLFLOW_TRACKING_URI=
1918
# MLFLOW_TRACKING_TOKEN=
2019
# MLFLOW_EXPERIMENT_NAME=
2120
# MLFLOW_TRACKING_INSECURE_TLS=
2221
# MLFLOW_WORKSPACE=
23-
# MLFLOW_TRACKING_AUTH= # Use Kubernetes service account for authentication (if running inside the cluster)
22+
# MLFLOW_TRACKING_AUTH= # Use Kubernetes service account for authentication (if running inside the cluster)
Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
1-
# LlamaIndex Websearch Agent
2-
# Use Python 3.12 slim image as base
3-
FROM python:3.12-slim
1+
# Use Red Hat UBI9 Python 3.12 image (no Docker Hub rate limits on OpenShift)
2+
# To update: docker pull registry.access.redhat.com/ubi9/python-312:latest
3+
# docker inspect --format='{{index .RepoDigests 0}}' registry.access.redhat.com/ubi9/python-312:latest
4+
FROM registry.access.redhat.com/ubi9/python-312@sha256:e95978812895b9abb2bdc109b501078da2a47c8dbb9fa23758af40ed50ab6023
5+
WORKDIR /opt/app-root/src
46

5-
# Set working directory
6-
WORKDIR /app
7+
# Switch to root for installing dependencies
8+
USER 0
79

8-
# Create a non-privileged user for OpenShift
9-
# UID 1001 is commonly used in OpenShift deployments
10-
RUN groupadd -r appuser && useradd -r -u 1001 -g appuser appuser
11-
12-
# Install uv for fast dependency management
13-
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
10+
# Install uv for fast dependency management (v0.11.1)
11+
# To update: docker pull ghcr.io/astral-sh/uv:latest
12+
# docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/astral-sh/uv:latest
13+
COPY --from=ghcr.io/astral-sh/uv@sha256:fc93e9ecd7218e9ec8fba117af89348eef8fd2463c50c13347478769aaedd0ce /uv /usr/local/bin/uv
1414

1515
# Copy project files for dependency installation
1616
COPY pyproject.toml .
1717
COPY src/ ./src/
1818

1919
# Install the project and its dependencies using uv
20-
RUN uv pip install --system --no-cache .
20+
RUN uv pip install --no-cache ".[tracing]"
2121

2222
# Copy the application entrypoint, playground UI, and images
2323
COPY main.py .
2424
COPY playground/ ./playground/
2525
COPY images/ ./images/
2626

27-
# Change ownership of the app directory to the non-privileged user
28-
RUN chown -R appuser:appuser /app
27+
# Ensure app directory is owned by default user (UID 1001)
28+
RUN chown -R 1001:0 /opt/app-root/src && chmod -R g=u /opt/app-root/src
2929

30-
# Switch to non-privileged user
31-
USER appuser
30+
# Switch back to default non-root user
31+
USER 1001
3232

3333
# Expose port 8080 (OpenShift standard)
3434
EXPOSE 8080
3535

3636
# Set environment variables
3737
ENV PORT=8080
38-
ENV PYTHONPATH=/app:/app/src
38+
ENV PYTHONPATH=/opt/app-root/src
3939

40-
# Run the application
41-
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
40+
# Run the application — reads PORT at runtime
41+
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port ${PORT}"]
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
SHELL := bash
2+
AGENT_NAME := $(shell python3 -c "import re; print(re.search(r'^name:\s*(.+)', open('agent.yaml').read(), re.M).group(1).strip())")
3+
CHART_DIR := ../../../charts/agent
4+
VALUES_FILE := values.yaml
5+
CONTAINER_CLI := $(shell command -v podman 2>/dev/null || command -v docker 2>/dev/null)
6+
7+
.PHONY: init run run-cli build push build-openshift deploy undeploy test dry-run help
8+
9+
help: ## Show this help
10+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-12s %s\n", $$1, $$2}'
11+
12+
init: ## Copy .env.example to .env for configuration
13+
@if [ ! -f .env ]; then cp .env.example .env && echo "Created .env from .env.example — edit it with your configuration"; else echo ".env already exists — skipping"; fi
14+
15+
run: ## Run agent locally with hot-reload
16+
@set -a && source .env && set +a && \
17+
uv run uvicorn main:app --host 0.0.0.0 --port $${PORT:-8000} --reload --reload-exclude .venv
18+
19+
run-cli: ## Run interactive CLI chat (no web server)
20+
@set -a && source .env && set +a && \
21+
cd examples && uv run python execute_ai_service_locally.py
22+
23+
build: ## Build container image locally (podman/docker)
24+
@[ -n "$(CONTAINER_CLI)" ] || { echo "ERROR: neither podman nor docker found in PATH"; exit 1; } && \
25+
source .env && \
26+
[ -n "$${CONTAINER_IMAGE}" ] || { echo "ERROR: CONTAINER_IMAGE is not set in .env"; exit 1; } && \
27+
cp -r ../../../images ./images && trap 'rm -rf ./images' EXIT && \
28+
$(CONTAINER_CLI) build --platform linux/amd64 -t "$${CONTAINER_IMAGE}" -f Dockerfile .
29+
30+
push: ## Push container image to registry
31+
@[ -n "$(CONTAINER_CLI)" ] || { echo "ERROR: neither podman nor docker found in PATH"; exit 1; } && \
32+
source .env && \
33+
[ -n "$${CONTAINER_IMAGE}" ] || { echo "ERROR: CONTAINER_IMAGE is not set in .env"; exit 1; } && \
34+
$(CONTAINER_CLI) push "$${CONTAINER_IMAGE}"
35+
36+
build-openshift: ## Build image in-cluster via OpenShift BuildConfig (no podman/docker needed)
37+
@cp -r ../../../images ./images && trap 'rm -rf ./images' EXIT && \
38+
oc new-build --strategy=docker --binary --name=$(AGENT_NAME) --to=$(AGENT_NAME):latest 2>/dev/null || true && \
39+
oc start-build $(AGENT_NAME) --from-dir=. --follow && \
40+
NS=$$(oc project -q) && \
41+
echo "" && \
42+
echo "Image built. To deploy, set in .env:" && \
43+
echo " CONTAINER_IMAGE=image-registry.openshift-image-registry.svc:5000/$$NS/$(AGENT_NAME):latest"
44+
45+
_check-env:
46+
@set -a && source .env 2>/dev/null && set +a; \
47+
missing=""; \
48+
for var in $$(sed -n '/^ *required:/,/^ *[a-z]/{ /^ *- /s/^ *- *//p; }' agent.yaml); do \
49+
eval val="\$$$$var"; \
50+
[ -n "$$val" ] || missing="$$missing $$var"; \
51+
done; \
52+
[ -z "$$missing" ] || { echo "ERROR: Missing required env vars:$$missing"; exit 1; }
53+
54+
deploy: _check-env ## Deploy to OpenShift/K8s via Helm
55+
@source .env && \
56+
[ -n "$${CONTAINER_IMAGE}" ] || { echo "ERROR: CONTAINER_IMAGE is not set in .env"; exit 1; } && \
57+
case "$${CONTAINER_IMAGE}" in *:*) IMAGE_REPO="$${CONTAINER_IMAGE%:*}"; IMAGE_TAG="$${CONTAINER_IMAGE##*:}";; *) IMAGE_REPO="$${CONTAINER_IMAGE}"; IMAGE_TAG="latest";; esac && \
58+
trap 'rm -f .helm-secrets.yaml' EXIT && \
59+
umask 077 && \
60+
printf 'secrets:\n apiKey: "%s"\n' "$${API_KEY}" > .helm-secrets.yaml && \
61+
helm upgrade --install $(AGENT_NAME) $(CHART_DIR) \
62+
-f $(VALUES_FILE) \
63+
-f .helm-secrets.yaml \
64+
--set image.repository="$${IMAGE_REPO}" \
65+
--set image.tag="$${IMAGE_TAG}" \
66+
--set env.BASE_URL="$${BASE_URL}" \
67+
--set env.MODEL_ID="$${MODEL_ID}" \
68+
$${MLFLOW_TRACKING_URI:+--set env.MLFLOW_TRACKING_URI="$${MLFLOW_TRACKING_URI}"} \
69+
$${MLFLOW_TRACKING_TOKEN:+--set env.MLFLOW_TRACKING_TOKEN="$${MLFLOW_TRACKING_TOKEN}"} \
70+
$${MLFLOW_EXPERIMENT_NAME:+--set env.MLFLOW_EXPERIMENT_NAME="$${MLFLOW_EXPERIMENT_NAME}"} \
71+
$${MLFLOW_TRACKING_INSECURE_TLS:+--set env.MLFLOW_TRACKING_INSECURE_TLS="$${MLFLOW_TRACKING_INSECURE_TLS}"} \
72+
$${MLFLOW_WORKSPACE:+--set env.MLFLOW_WORKSPACE="$${MLFLOW_WORKSPACE}"} && \
73+
if command -v oc >/dev/null 2>&1; then \
74+
ROUTE=$$(oc get route $(AGENT_NAME) -o jsonpath='{.spec.host}' 2>/dev/null || true); \
75+
if [ -n "$$ROUTE" ]; then echo "" && echo "Agent is available at: https://$$ROUTE" && echo "Note: It may take about a minute for the application to become available."; fi; \
76+
fi
77+
78+
dry-run: _check-env ## Render Helm templates without deploying
79+
@source .env && \
80+
[ -n "$${CONTAINER_IMAGE}" ] || { echo "ERROR: CONTAINER_IMAGE is not set in .env"; exit 1; } && \
81+
case "$${CONTAINER_IMAGE}" in *:*) IMAGE_REPO="$${CONTAINER_IMAGE%:*}"; IMAGE_TAG="$${CONTAINER_IMAGE##*:}";; *) IMAGE_REPO="$${CONTAINER_IMAGE}"; IMAGE_TAG="latest";; esac && \
82+
helm template $(AGENT_NAME) $(CHART_DIR) \
83+
-f $(VALUES_FILE) \
84+
--set secrets.apiKey="REDACTED" \
85+
--set image.repository="$${IMAGE_REPO}" \
86+
--set image.tag="$${IMAGE_TAG}" \
87+
--set env.BASE_URL="$${BASE_URL}" \
88+
--set env.MODEL_ID="$${MODEL_ID}"
89+
90+
undeploy: ## Remove deployment from cluster
91+
helm uninstall $(AGENT_NAME)
92+
93+
test: ## Run tests
94+
uv run --extra dev python -m pytest tests/

0 commit comments

Comments
 (0)