feat: ability to use postgres as store for starter distro

The starter distribution now comes with all the required packages to
support persistent stores—like the agent store, metadata, and
inference—using PostgreSQL.  We’ve added a new run YAML file,
run-with-postgres-store.yaml, to make this setup easy. The file is
included in the container image, so users can simply override the
entrypoint to use the Postgres-specific config.  The documentation has
been updated with instructions on how to do that.

Closes: #2619
Signed-off-by: Sébastien Han <seb@redhat.com>
This commit is contained in:
Sébastien Han 2025-07-22 11:31:30 +02:00
parent b6cb817897
commit 046d85fa79
No known key found for this signature in database
15 changed files with 426 additions and 336 deletions

View file

@ -23,6 +23,9 @@ RUN_CONFIG_PATH=/app/run.yaml
BUILD_CONTEXT_DIR=$(pwd)
# Placeholder for template files
TEMPLATE_FILES=()
set -euo pipefail
# Define color codes
@ -324,14 +327,19 @@ fi
RUN pip uninstall -y uv
EOF
# If a run config is provided, we use the --config flag
# Copy the entrypoint script to build context and then into the container
cp llama_stack/core/entrypoint.sh "$BUILD_CONTEXT_DIR/entrypoint.sh"
add_to_container << EOF
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
EOF
if [[ -n "$run_config" ]]; then
add_to_container << EOF
ENTRYPOINT ["python", "-m", "llama_stack.core.server.server", "$RUN_CONFIG_PATH"]
ENTRYPOINT ["/app/entrypoint.sh", "$RUN_CONFIG_PATH"]
EOF
elif [[ "$distro_or_config" != *.yaml ]]; then
add_to_container << EOF
ENTRYPOINT ["python", "-m", "llama_stack.core.server.server", "$distro_or_config"]
ENTRYPOINT ["/app/entrypoint.sh", "$distro_or_config"]
EOF
fi
@ -404,7 +412,8 @@ $CONTAINER_BINARY build \
"$BUILD_CONTEXT_DIR"
# clean up tmp/configs
rm -rf "$BUILD_CONTEXT_DIR/run.yaml" "$TEMP_DIR"
rm -rf "$BUILD_CONTEXT_DIR/run.yaml" "$BUILD_CONTEXT_DIR/entrypoint.sh" "$TEMP_DIR" "${TEMPLATE_FILES[@]}"
set +x
echo "Success!"

38
llama_stack/core/entrypoint.sh Executable file
View file

@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
set -euo pipefail
determine_server_command() {
local server_args=()
# The env variable will take precedence over the config file
if [ -n "${ENABLE_POSTGRES_STORE:-}" ]; then
server_args=("python3" "-m" "llama_stack.core.server.server" "starter::run-with-postgres-store")
elif [ -n "$1" ]; then
server_args=("python3" "-m" "llama_stack.core.server.server" "$1")
fi
echo "${server_args[@]}"
}
main() {
echo "Starting Llama Stack server..."
local server_command
server_command=$(determine_server_command "$@")
if [[ -z "$server_command" ]]; then
echo "Error: Could not determine server command"
exit 1
fi
printf "Executing: %s\n" "$server_command"
exec $server_command
}
main "$@"

View file

@ -52,7 +52,17 @@ def resolve_config_or_distro(
logger.info(f"Using distribution: {distro_config}")
return distro_config
# Strategy 3: Try as built distribution name
# Strategy 3: Try as distro config path (if no .yaml extension and contains a slash)
# eg: starter::run-with-postgres-store.yaml
# Use :: to avoid slash and confusion with a filesystem path
if "::" in config_or_distro:
distro_name, config_name = config_or_distro.split("::")
distro_config = _get_distro_config_path(distro_name, config_name)
if distro_config.exists():
logger.info(f"Using distribution: {distro_config}")
return distro_config
# Strategy 4: Try as built distribution name
distrib_config = DISTRIBS_BASE_DIR / f"llamastack-{config_or_distro}" / f"{config_or_distro}-{mode}.yaml"
if distrib_config.exists():
logger.info(f"Using built distribution: {distrib_config}")
@ -63,12 +73,15 @@ def resolve_config_or_distro(
logger.info(f"Using built distribution: {distrib_config}")
return distrib_config
# Strategy 4: Failed - provide helpful error
# Strategy 5: Failed - provide helpful error
raise ValueError(_format_resolution_error(config_or_distro, mode))
def _get_distro_config_path(distro_name: str, mode: Mode) -> Path:
def _get_distro_config_path(distro_name: str, mode: str) -> Path:
"""Get the config file path for a distro."""
if mode.endswith(".yaml"):
return DISTRO_DIR / distro_name / mode
return DISTRO_DIR / distro_name / f"{mode}.yaml"

View file

@ -84,6 +84,14 @@ def run_command(command: list[str]) -> int:
text=True,
check=False,
)
# Print stdout and stderr if command failed
if result.returncode != 0:
if result.stdout:
log.error(f"STDOUT: {result.stdout}")
if result.stderr:
log.error(f"STDERR: {result.stderr}")
return result.returncode
except subprocess.SubprocessError as e:
log.error(f"Subprocess error: {e}")