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
10 changes: 8 additions & 2 deletions docs/devel_doc/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -12717,12 +12717,15 @@
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "PostgreSQL port",
"description": "PostgreSQL port for remote::pgvector. Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector."
"description": "PostgreSQL port for remote::pgvector. Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector. Accepts string placeholders and integer values."
},
"db": {
"anyOf": [
Expand Down Expand Up @@ -17519,12 +17522,15 @@
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "PostgreSQL port",
"description": "PostgreSQL port. Defaults to ${env.POSTGRES_PORT}."
"description": "PostgreSQL port. Defaults to ${env.POSTGRES_PORT}. Accepts string placeholders and integer values."
},
"db": {
"anyOf": [
Expand Down
10 changes: 6 additions & 4 deletions src/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2085,11 +2085,12 @@ class ByokRag(ConfigurationBase):
"Defaults to ${env.POSTGRES_HOST} when rag_type is remote::pgvector.",
)

port: Optional[str] = Field(
port: Optional[str | int] = Field(
default=None,
title="PostgreSQL port",
description="PostgreSQL port for remote::pgvector. "
"Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector.",
"Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector. "
"Accepts string placeholders and integer values.",
)

db: Optional[str] = Field(
Expand Down Expand Up @@ -2153,10 +2154,11 @@ class PgvectorVectorStoreProviderConfig(ConfigurationBase):
description="PostgreSQL host. Defaults to ${env.POSTGRES_HOST}.",
)

port: Optional[str] = Field(
port: Optional[str | int] = Field(
default=None,
title="PostgreSQL port",
description="PostgreSQL port. Defaults to ${env.POSTGRES_PORT}.",
description="PostgreSQL port. Defaults to ${env.POSTGRES_PORT}. "
"Accepts string placeholders and integer values.",
)

db: Optional[str] = Field(
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/models/config/test_byok_rag.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,21 @@ def test_byok_rag_pgvector_custom_connection_fields() -> None:
assert store.password.get_secret_value() == "secret" # pylint: disable=no-member


def test_byok_rag_pgvector_accepts_int_port() -> None:
"""Int port (from replace_env_vars coercion) must validate for pgvector."""
store = ByokRag(
rag_id="pg_store",
rag_type="remote::pgvector",
vector_db_id="vs_pg",
host="db.example.com",
port=5432,
db="my_knowledge",
user="admin",
password="secret",
)
assert store.port == 5432


def test_byok_rag_pgvector_partial_overrides() -> None:
"""Test pgvector fills only missing connection fields with defaults."""
store = ByokRag(
Expand Down
67 changes: 67 additions & 0 deletions tests/unit/models/config/test_vector_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import pytest
import yaml
from llama_stack.core.stack import replace_env_vars
from pydantic import SecretStr, TypeAdapter, ValidationError

from models.config import Configuration, VectorStoreProvider
Expand Down Expand Up @@ -93,6 +94,72 @@ def test_pgvector_applies_env_defaults() -> None:
assert provider.config.password == SecretStr("${env.POSTGRES_PASSWORD}")


def test_pgvector_accepts_int_port_from_env_substitution(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Int port after replace_env_vars type coercion must validate.

Llama Stack's replace_env_vars converts digit-only env values to int via
_convert_string_to_proper_type. LCORE loads config through that helper, so
port must accept int as well as str / ${env.*} placeholders.

Parameters:
monkeypatch: Fixture that sets and restores environment variables.
"""
Comment thread
coderabbitai[bot] marked this conversation as resolved.
monkeypatch.setenv("PGVECTOR_PORT", "5432")
resolved = replace_env_vars({"port": "${env.PGVECTOR_PORT:=5432}"})
assert resolved["port"] == 5432
assert isinstance(resolved["port"], int)

provider = _PROVIDER_ADAPTER.validate_python(
{
"id": "nb-pg",
"type": "pgvector",
"embedding_model": "/emb",
"embedding_dimension": 768,
"config": {
"host": "db.example.com",
"port": resolved["port"],
"db": "vectors",
"user": "pguser",
"password": "secret",
},
}
)
assert provider.config.port == 5432


def test_pgvector_accepts_int_port_from_env_default(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Unset env with :=default still coerces the default to int and validates.

Parameters:
monkeypatch: Fixture that removes and restores environment variables.
"""
monkeypatch.delenv("PGVECTOR_PORT", raising=False)
resolved = replace_env_vars({"port": "${env.PGVECTOR_PORT:=5432}"})
assert resolved["port"] == 5432
assert isinstance(resolved["port"], int)

provider = _PROVIDER_ADAPTER.validate_python(
{
"id": "nb-pg",
"type": "pgvector",
"embedding_model": "/emb",
"embedding_dimension": 768,
"config": {
"host": "db.example.com",
"port": resolved["port"],
"db": "vectors",
"user": "pguser",
"password": "secret",
},
}
)
assert provider.config.port == 5432


def test_rejects_byok_prefix_id() -> None:
"""Provider id must not use the byok_ prefix reserved for BYOK RAG."""
with pytest.raises(ValidationError, match="byok_"):
Expand Down
21 changes: 18 additions & 3 deletions tests/unit/utils/test_models_dumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,10 +541,19 @@ def test_dump_models(tmpdir: Path) -> None:
"title": "PostgreSQL host"
},
"port": {
"type": "string",
"nullable": true,
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "PostgreSQL port for remote::pgvector. Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector.",
"description": "PostgreSQL port for remote::pgvector. Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector. Accepts string placeholders and integer values.",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"title": "PostgreSQL port"
},
"db": {
Expand Down Expand Up @@ -9155,6 +9164,12 @@ def test_dump_models(tmpdir: Path) -> None:
schemas = components["schemas"]
assert schemas is not None

# ByokRag.port accepts str placeholders, int values, and null.
port_schema = schemas["ByokRag"]["properties"]["port"]
assert {"type": "string"} in port_schema["anyOf"]
assert {"type": "integer"} in port_schema["anyOf"]
assert {"type": "null"} in port_schema["anyOf"]

# list of schemas expected in a dump
expected_schemas = (
"A2AStateConfiguration",
Expand Down
Loading