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
6 changes: 3 additions & 3 deletions .github/workflows/image.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI/CD App, Llama Stack Image Build and Push
name: CI/CD App, OGX Image Build and Push

on:
push:
Expand All @@ -23,8 +23,8 @@ jobs:
include:
- image_name: agentic-sample
containerfile: Containerfile
- image_name: agentic-llama-stack
containerfile: Containerfile.llama-stack
- image_name: agentic-ogx
containerfile: Containerfile.ogx

steps:
- name: Free Disk Space
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ ENV/
.env
.env.local

# Llama Stack local data
# OGX local data
.llama/
.data/
start-local-ogx/data/

# Temporary files
*.tmp
Expand Down
14 changes: 7 additions & 7 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Llama Stack Agentic Application Container
# OGX Agentic Application Container
# Multi-stage build for smaller, more secure image

# =============================================================================
Expand Down Expand Up @@ -78,10 +78,10 @@ USER 1001
CMD ["streamlit", "run", "streamlit_app.py", "--server.address=0.0.0.0"]

# Labels
LABEL com.redhat.component=llama-stack-agentic-app \
description="Llama Stack Agentic AI Workflow Application" \
io.k8s.description="Llama Stack Agentic AI Workflow Application" \
io.k8s.display-name="Llama Stack Agentic App" \
io.openshift.tags="ai,llama-stack,streamlit,agents,rag" \
name=llama-stack-agentic-app \
LABEL com.redhat.component=ogx-agentic-app \
description="OGX Agentic AI Workflow Application" \
io.k8s.description="OGX Agentic AI Workflow Application" \
io.k8s.display-name="OGX Agentic App" \
io.openshift.tags="ai,ogx,streamlit,agents,rag" \
name=ogx-agentic-app \
summary="Streamlit application for agentic AI workflows"
38 changes: 21 additions & 17 deletions Containerfile.llama-stack → Containerfile.ogx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Llama Stack Server Container
# OGX Server Container
# Multi-stage build for smaller, more secure image
# Configuration uses ${env.VAR} syntax - resolved at runtime by Llama Stack
# Configuration uses ${env.VAR} syntax - resolved at runtime by OGX

# =============================================================================
# BUILDER STAGE
Expand All @@ -19,11 +19,12 @@ RUN dnf install -y gcc python3-devel make && \
dnf clean all && \
pip3.12 install uv

# Copy Llama Stack specific dependency specification
COPY ./pyproject.llama-stack.toml ./pyproject.toml
# Copy OGX specific dependency specification
COPY ./start-local-ogx/pyproject.toml ./pyproject.toml
COPY ./start-local-ogx/uv.lock ./uv.lock

# Install Python dependencies
RUN uv sync --no-dev
# Install Python dependencies (deps only; the ogx-server project has no package to build)
RUN uv sync --no-dev --no-install-project

# =============================================================================
# RUNTIME STAGE
Expand All @@ -44,9 +45,9 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
# Copy virtual environment from builder
COPY --from=builder --chown=1001:1001 /app-root/.venv ./.venv

# Copy Llama Stack configuration
# Copy OGX configuration
# Uses ${env.VAR} syntax - resolved at runtime
COPY --chown=1001:1001 ./run.yaml ./
COPY --chown=1001:1001 ./start-local-ogx/run.yaml ./

# Create data directories for runtime
USER root
Expand All @@ -58,19 +59,22 @@ RUN mkdir -p /data /data/files && \
RUN mkdir -p /licenses
COPY LICENSE /licenses/

# Expose Llama Stack server port
# Expose OGX server port
EXPOSE 8321

USER 1001

# Default command - run.yaml is in /app-root
CMD ["llama", "stack", "run", "/app-root/run.yaml"]
# --insecure: serve plain HTTP. The OGX server is reached only in-cluster
# (app pod -> ogx sidecar on port 8321); no external route is exposed to it.
# OGX 1.2.5+ requires TLS certs otherwise and exits on startup.
CMD ["ogx", "stack", "run", "/app-root/run.yaml", "--insecure"]

# Labels
LABEL com.redhat.component=llama-stack-agentic \
description="Llama Stack Agentic AI Server" \
io.k8s.description="Llama Stack Agentic AI Server" \
io.k8s.display-name="Llama Stack Agentic" \
io.openshift.tags="ai,llama-stack,inference,agents" \
name=llama-stack-agentic \
summary="Llama Stack server for agentic AI workflows"
LABEL com.redhat.component=ogx-agentic \
description="OGX Agentic AI Server" \
io.k8s.description="OGX Agentic AI Server" \
io.k8s.display-name="OGX Agentic" \
io.openshift.tags="ai,ogx,inference,agents" \
name=ogx-agentic \
summary="OGX server for agentic AI workflows"
61 changes: 36 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
![Python 3.13](https://img.shields.io/badge/python-3.13-blue.svg)
![Tests](https://img.shields.io/badge/tests-passing-success.svg)

This AI Agentic application provides a prototype "developer portal" chat interface which employs dynamic, stateful, workflow orchestration via both LangGraph and Llama Stack which takes customer questions and dynamically routes to different AI Agents, and call different agentic tools, based on the user's prompt and how it is classified.
This AI Agentic application provides a prototype "developer portal" chat interface which employs dynamic, stateful, workflow orchestration via both LangGraph and OGX which takes customer questions and dynamically routes to different AI Agents, and call different agentic tools, based on the user's prompt and how it is classified.

When progressing through the orchestration, rather than making any AI Related REST invocations directly against running AI Models, the model interactions all flow back and forth through a locally running Llama Stack instance and its Responses API compatibility layer.
When progressing through the orchestration, rather than making any AI Related REST invocations directly against running AI Models, the model interactions all flow back and forth through a locally running OGX instance and its Responses API compatibility layer.

The sections below provide a guide

- for Llama Stack setup and running of the application
- for OGX setup and running of the application
- with details on how elements of the application are implemented

A hint on what you will find

- Use of LangGraph's [Graph API](https://docs.langchain.com/oss/python/langgraph/graph-api)
- Use of [OpenAI Moderations](https://platform.openai.com/docs/guides/moderation) provided by Llama Stack's [OpenAI-compatible API endpoint](https://llamastack.github.io/docs/providers/openai)
- Use of [OpenAI Moderations](https://platform.openai.com/docs/guides/moderation) provided by OGX's [OpenAI-compatible API endpoint](https://ogx.github.io/docs/providers/openai)
- Use of Red Hat's [Kubernetes MCP Server](https://developers.redhat.com/articles/2025/09/25/kubernetes-mcp-server-ai-powered-cluster-management)
- Use of GitHub's [Remote GitHub MCP Server](https://github.com/mcp/github/github-mcp-server)

Expand All @@ -30,17 +30,24 @@ For example

```bash
# only required if you do not override the GUARDRAIL_MODEL environment variable
ollama pull llama-guard3:1b
ollama pull llama-guard3.1:8b
# embedding model required by the vector store / RAG file search; the server
# refuses to start without it
ollama pull all-minilm:l6-v2
ollama serve
```

proved sufficient for providing a model to pass the moderation / guardrail checks.

You can also employ `ollama run llama-guard3:1b --keepalive 60m` for each of those models if ollama is already running. When their
The OGX server validates its configured embedding model (`all-minilm:l6-v2`) at
startup, so pulling it is mandatory for a local run even if you are not using RAG
file search.

You can also employ `ollama run llama-guard3.1:8b --keepalive 60m` for each of those models if ollama is already running. When their
startup completes, type `/bye` and the provided prompt to return to your terminal.

We also recommend you find a model that grades well for tool calling, register it with Llama Stack, and set the
`MCP_TOOL_MODEL` environment variable to the model's ID as seen when running `llama_stack_client models list`.
We also recommend you find a model that grades well for tool calling, register it with OGX, and set the
`MCP_TOOL_MODEL` environment variable to the model's ID as seen when running `ogx_client models list`.

For maximum flexibility, the application breaks down the use of AI models along these lines:

Expand All @@ -49,13 +56,14 @@ For maximum flexibility, the application breaks down the use of AI models along

In our testing, the following models achieved tolerable performance for both `INFERENCE_MODEL` AND `MCP_TOOL_MODEL`:

- `qwen3-8b-fp8` deployed using OpenShift AI in our local environment, where we leverage the Llama Stack vllm provider
- `gemini-2.5-pro` via the Google public offering, where we leverage the LlamaStack gemini provider.
- `gpt-4o` and `gpt-4o-mini` via the OpenAI public offering, where we leverage the LlamaStack openai provider
- `ollama/llama3.1:8b` via local Ollama run.
- `qwen3-8b-fp8` deployed using OpenShift AI in our local environment, where we leverage the OGX vllm provider
- `gemini-2.5-pro` via the Google public offering, where we leverage the Ogx gemini provider.
- `gpt-4o` and `gpt-4o-mini` via the OpenAI public offering, where we leverage the Ogx openai provider

Visit the [run.yaml file](./run.yaml) for the environment variables leveraged with starting up those Llama Stack providers.
Visit the [run.yaml file](./run.yaml) for the environment variables leveraged with starting up those OGX providers.

### Update your Llama Stack config to access your existing models
### Update your OGX config to access your existing models

### Setup your Virtual Environment

Expand All @@ -66,16 +74,19 @@ uv sync
source .venv/bin/activate
```

### Run Llama Stack
### Run OGX

We have provided a custom run.yaml file to specify the required providers. Use the following command to run the Llama Stack with the custom configuration file.
We have provided a custom run.yaml file to specify the required providers. Use the following command to run the OGX with the custom configuration file.
The snippet below assumes you are in the top directory of your clone of this repository.

```
cd start-local-llamastack
cd start-local-ogx
uv sync
source .venv/bin/activate
uv run llama stack run run.yaml
# The Ollama provider only registers when OLLAMA_URL is set; export it before
# starting so the embedding/inference models are available.
export OLLAMA_URL=http://localhost:11434/v1
uv run ogx run run.yaml --insecure
```

### Launch Kubernetes MCP Server
Expand Down Expand Up @@ -109,13 +120,13 @@ Lastly, provide your GitHub ID via the `GITHUB_ID` environment variable.

The sample will create issues against the repository indicated by `GITHUB_URL` and assign the issue to the ID indicated by `GITHUB_ID`.

### Validate Llama Stack Setup
### Validate OGX Setup

Open a new terminal and navigate to the project root directory. Activate your existing virtual environment and use the CLI tool to test your setup.

```bash
source .venv/bin/activate
uv run llama-stack-client configure --endpoint http://localhost:8321 --api-key none
uv run ogx-client configure --endpoint http://localhost:8321 --api-key none
```

## Run the application Code
Expand Down Expand Up @@ -174,7 +185,7 @@ Three LangGraph `StateGraphs` encompass the orchestration workflow:
LangGraph provides various API for printing all the nodes and edges of the `StateGraph` you create. The sample leverages
one of those API to print all three `StateGraphs` on startup.

The sample interacts with AI models through Llama Stack's Response API, which provides OpenAI-compatible endpoints for model inference and structured output generation.
The sample interacts with AI models through OGX's Response API, which provides OpenAI-compatible endpoints for model inference and structured output generation.

The sample then leverages dictionary based schema and prompting via the `with_structured_output` function call to derive:

Expand All @@ -188,10 +199,10 @@ Certain nodes throughout the multilayered orchestration will call either:

## MCP Tool calling

The sample uses Llama Stack's OpenAI-compatible API endpoint, specifically the Responses API, to prompt the LLM so that a
The sample uses OGX's OpenAI-compatible API endpoint, specifically the Responses API, to prompt the LLM so that a
call to the desired MCP tool is made.

Direct MCP tool calling through Llama Stack's OpenAI Responses API has been successfully implemented in this sample.
Direct MCP tool calling through OGX's OpenAI Responses API has been successfully implemented in this sample.

### Qwen3-8b-fp8

Expand All @@ -203,7 +214,7 @@ Specifically:
'error': 'Expecting value: line 1 column 1 (char 0)', 'type': 'invalid_tool_call'
```

- with the OpenAI Chat Completion API enabled, the 'invoke' or 'ainvoke' call completes without error, but the MCP server is not called, and the Llama Stack server has logs like:
- with the OpenAI Chat Completion API enabled, the 'invoke' or 'ainvoke' call completes without error, but the MCP server is not called, and the OGX server has logs like:

```
INFO 2025-10-19 12:09:39,133 console_span_processor:48 telemetry:
Expand Down Expand Up @@ -234,9 +245,9 @@ call of the MCP server do appear to occur.
content=[{'arguments': '{}', 'call_id': 'call_rpuxUFZg81ON7AdMZS8SUNig', 'name': 'namespaces_list', 'type': 'function_call', 'id': 'fc_7510dc25-e069-4990-b1c0-4c5891af8f98', 'status': 'completed'}] additional_kwargs={} response_metadata={'id': 'resp-25a423e0-b893-4662-a1ae-08570175afae', 'created_at': 1761497399.0, 'model': 'openai/gpt-4o', 'object': 'response', 'status': 'completed', 'model_provider': 'openai', 'model_name': 'openai/gpt-4o'} id='resp-25a423e0-b893-4662-a1ae-08570175afae' tool_calls=[{'name': 'namespaces_list', 'args': {}, 'id': 'call_rpuxUFZg81ON7AdMZS8SUNig', 'type': 'tool_call'}]
```

In addition to running through Llama Stack, an attempt was also made to run against [https://api.openai.com/v1/responses](https://api.openai.com/v1/responses) directly,
In addition to running through OGX, an attempt was also made to run against [https://api.openai.com/v1/responses](https://api.openai.com/v1/responses) directly,
but again without success.

## Safety / Guardrails

This sample uses Llama Stack's OpenAI Moderations API for content safety and guardrails. The moderations API provides built-in checks that work with models like `ollama/llama-guard3:8b` to flag inappropriate content and ensure safe interactions.
This sample uses OGX's OpenAI Moderations API for content safety and guardrails. The moderations API provides built-in checks that work with models like `ollama/llama-guard3:8b` to flag inappropriate content and ensure safe interactions.
4 changes: 2 additions & 2 deletions all.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
apiVersion: backstage.io/v1alpha1
kind: Location
metadata:
name: llama-stack-agentic-all
description: Llama Stack Agentic Sample
name: ogx-agentic-all
description: OGX Agentic Sample
spec:
targets:
- ./template/template.yaml
2 changes: 1 addition & 1 deletion e2e/API/developer-hub-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class DeveloperHubClient {
githubServer: options.githubServer,
repoOwner: options.repoOwner,
repoName: options.repoName,
llamaStackSecretName: options.llamaStackSecretName,
ogxSecretName: options.ogxSecretName,
platformCredentialsSecretName: options.platformCredentialsSecretName,
secretsAcknowledgment: options.secretsAcknowledgment,
namespace: options.namespace,
Expand Down
2 changes: 1 addition & 1 deletion e2e/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface LlsTemplateValues {
githubServer: string
repoOwner: string
repoName: string
llamaStackSecretName: string
ogxSecretName: string
platformCredentialsSecretName: string
secretsAcknowledgment: boolean
namespace: string,
Expand Down
2 changes: 1 addition & 1 deletion e2e/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# E2E tests for llama stack agentic template
# E2E tests for ogx agentic template

Basic test suite that to verify the lls agentic app template runs and creates the appropriate resources.

Expand Down
2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "e2e-tests",
"version": "1.0.0",
"description": "llama-stack template E2E tests",
"description": "ogx template E2E tests",
"author": "Red Hat",
"scripts": {
"test": "jest --config=jest.config.ts"
Expand Down
10 changes: 5 additions & 5 deletions e2e/tests/lls-template.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { KubeClient } from '../API/k8s-client';
import { LlsTemplateValues } from '../API/types';
import { GitHubClient } from '../API/git-client';

describe('Llama stack agentic sample template', () => {
describe('OGX agentic sample template', () => {
const templateUrl = 'https://github.com/redhat-developer/rhdh-llama-stack-agentic-sample/blob/main/template/template.yaml';
const namespace = process.env.APP_NAMESPACE || 'rhdh-app';
const templateName = 'llama-stack-agentic';
const templateName = 'ogx-agentic';

const appName = `lls-test-${Date.now()}`;
const llsSecret = 'llama-stack-secrets';
const llsSecret = 'ogx-secrets';
const platformSecret = 'platform-credentials';

const defaultRepo = `https://github.com/${process.env.GITOPS_GIT_ORG}/${appName}`;
Expand All @@ -27,7 +27,7 @@ describe('Llama stack agentic sample template', () => {
imageOrg: process.env.IMAGE_ORG || 'rhdh-pai-qe',
imageRegistry: 'quay.io',
inferenceModel: process.env.INFERENCE_MODEL || 'vllm/redhataiqwen3-8b-fp8-dynamic',
llamaStackSecretName: llsSecret,
ogxSecretName: llsSecret,
mcpToolModel: process.env.MCP_MODEL || 'vllm/redhataiqwen3-8b-fp8-dynamic',
name: appName,
namespace: namespace,
Expand Down Expand Up @@ -112,7 +112,7 @@ describe('Llama stack agentic sample template', () => {
const names = deployments.flatMap((deployment) => deployment.metadata?.name);

expect(names).toContain(templateValues.name);
expect(names).toContain(`${templateValues.name}-llama-stack`);
expect(names).toContain(`${templateValues.name}-ogx`);
expect(names).toContain(`${templateValues.name}-mcp-server`);
expect(names).toContain(`${templateValues.name}-ollama`);
});
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
[project]
name = "rhdh-llama-stack-agentic-sample"
version = "0.1.0"
description = "Llama-Stack LangGraph Customer Service Example"
description = "OGX LangGraph Customer Service Example"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"docling>=2.63.0",
"llama-stack-client>=0.3.1",
"llama-stack>=0.3.1",
# Python SDK used to call the OGX (Open GenAI Stack) server APIs.
"ogx-client==1.2.5",
# OpenAI SDK used for the Responses/Chat/Moderations compatibility endpoints.
"openai>=2.14.0,<3",
"pygithub>=2.8.1",
"pyyaml>=6.0.3",
"langgraph>=1.0.4",
Expand Down
Loading
Loading