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
11 changes: 9 additions & 2 deletions docs/en_US/ai_tools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Before using AI Reports, you must:

**Note:**

* AI Reports using cloud providers (Anthropic, OpenAI) require an active internet connection.
* AI Reports using cloud providers (Anthropic, OpenAI, GLM) require an active internet connection.
Local providers (Ollama, Docker Model Runner) do not require internet access.

* API usage may incur costs depending on your LLM provider's pricing model.
Expand Down Expand Up @@ -62,6 +62,13 @@ Select your preferred LLM provider from the dropdown:
* **API Key File**: Path to a file containing your OpenAI API key (obtain from https://platform.openai.com/). This path refers to the filesystem where the pgAdmin server is running (e.g., inside the container if using Docker). The ``~`` prefix is expanded to the home directory of the user running the pgAdmin server process. Optional when using a custom URL with a provider that does not require authentication.
* **Model**: Select from available GPT models (e.g., gpt-4).

**GLM (Z.ai)**
Use GLM models from Z.ai through the provider's OpenAI-compatible API.

* **API URL**: Custom API endpoint URL (leave empty for default: https://api.z.ai/api/paas/v4). Use https://api.z.ai/api/coding/paas/v4 with a Z.ai Coding Plan subscription.
* **API Key File**: Path to a file containing your Z.ai API key. This path refers to the filesystem where the pgAdmin server is running (e.g., inside the container if using Docker). The ``~`` prefix is expanded to the home directory of the user running the pgAdmin server process.
* **Model**: Select from available GLM models (e.g., glm-4.7). There is no default; a model must be selected before the AI features can be used.

**Ollama**
Use locally-hosted open-source models via Ollama. Requires a running Ollama instance.

Expand Down Expand Up @@ -237,7 +244,7 @@ Troubleshooting
You must establish a connection to the server or database before generating reports.

**API Connection Errors**
* Verify your API key is correct (for Anthropic and OpenAI).
* Verify your API key is correct (for Anthropic, OpenAI, and GLM).
* Check your internet connection (for cloud providers).
* For Ollama, ensure the Ollama server is running and accessible.
* For Docker Model Runner, ensure Docker Desktop 4.40+ is running with the model runner enabled.
Expand Down
20 changes: 19 additions & 1 deletion docs/en_US/preferences.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ in ``config.py``) for these preferences to be available.
Use the fields on the *AI* panel to configure your LLM provider:

* Use the *Default Provider* drop-down to select your LLM provider. Options include:
*Anthropic*, *OpenAI*, *Ollama*, or *Docker Model Runner*.
*Anthropic*, *OpenAI*, *GLM (Z.ai)*, *Ollama*, or *Docker Model Runner*.

**Anthropic Settings:**

Expand Down Expand Up @@ -81,6 +81,24 @@ Use the fields on the *AI* panel to configure your LLM provider:
refresh button to fetch the latest available models from your configured
endpoint.

**GLM (Z.ai) Settings:**

* Use the *API URL* field to set a custom API endpoint URL. Leave empty to use
the default GLM API (``https://api.z.ai/api/paas/v4``). Use
``https://api.z.ai/api/coding/paas/v4`` with a Z.ai Coding Plan
subscription.

* Use the *API Key File* field to specify the path to a file containing your
GLM API key. This path refers to the filesystem where the pgAdmin
server is running (e.g., inside the container if using Docker). The ``~``
prefix is expanded to the home directory of the user running the pgAdmin
server process.

* Use the *Model* field to select from the available GLM models. Click the
refresh button to fetch the latest available models from your configured
endpoint. There is no default model, so one must be selected before the
AI features can be used.

**Ollama Settings:**

* Use the *API URL* field to specify the Ollama server URL
Expand Down
22 changes: 21 additions & 1 deletion web/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,8 @@
# Default LLM Provider
# Specifies which LLM provider to use by default when LLM_ENABLED is True.
# Users can override this in their preferences.
# Valid values: 'anthropic', 'openai', 'ollama', 'docker', or '' (disabled)
# Valid values: 'anthropic', 'openai', 'glm', 'ollama', 'docker',
# or '' (disabled)
DEFAULT_LLM_PROVIDER = ''

# Anthropic Configuration
Expand Down Expand Up @@ -1064,6 +1065,24 @@
# Examples: gpt-4o, gpt-4o-mini, gpt-4-turbo
OPENAI_API_MODEL = ''

# GLM (Z.ai) Configuration
# URL for the GLM API endpoint. Leave empty to use the default
# (https://api.z.ai/api/paas/v4). Set this to
# https://api.z.ai/api/coding/paas/v4 for a Z.ai Coding Plan
GLM_API_URL = ''

# Path to a file containing the GLM API key. The file should contain
# only the API key with no additional whitespace or formatting.
# Default: ~/.zai-api-key
GLM_API_KEY_FILE = '~/.zai-api-key'

# The GLM model to use for AI features. There is no default: the models
# available differ between the Z.ai open platform, Coding Plan
# subscriptions and Zhipu's China platform, so one must be selected
# here or in the user's preferences.
# Examples: glm-4.6, glm-4.7, glm-5
GLM_API_MODEL = ''

# Ollama Configuration
# URL for the Ollama API endpoint. Leave empty to disable Ollama.
# Typical value: http://localhost:11434
Expand Down Expand Up @@ -1105,6 +1124,7 @@
ALLOWED_LLM_API_URLS = [
'https://api.anthropic.com:443',
'https://api.openai.com:443',
'https://api.z.ai:443',
# Loopback addresses on any port: covers Ollama (11434), Docker
# Model Runner (12434), LiteLLM (4000), vLLM (8000), LM Studio
# (1234), text-generation-webui (5000), and any self-hosted
Expand Down
4 changes: 3 additions & 1 deletion web/pgadmin/llm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ This module provides AI/LLM functionality for pgAdmin, including database securi

- **Anthropic Claude** (recommended)
- **OpenAI GPT**
- **GLM (Z.ai)**
- **Ollama** (local models)

## Configuration

Configure LLM providers in `config.py`:

- `DEFAULT_LLM_PROVIDER`: Set to 'anthropic', 'openai', or 'ollama'
- `DEFAULT_LLM_PROVIDER`: Set to 'anthropic', 'openai', 'glm', or 'ollama'
- `ANTHROPIC_API_KEY_FILE`: Path to file containing Anthropic API key
- `OPENAI_API_KEY_FILE`: Path to file containing OpenAI API key
- `GLM_API_KEY_FILE`: Path to file containing GLM API key
- `OLLAMA_API_URL`: URL for Ollama server (e.g., 'http://localhost:11434')
Comment on lines +23 to 27

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the required GLM model and Docker provider.

Line 23 omits supported docker; Lines 24-27 omit GLM_API_MODEL, although GLM initialization fails until a model is selected. Add both to keep config-file setup usable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/pgadmin/llm/README.md` around lines 23 - 27, Update the configuration
list in the LLM README to include docker among the supported
DEFAULT_LLM_PROVIDER values and document GLM_API_MODEL as required for GLM
initialization, alongside GLM_API_KEY_FILE.


If API keys are not found, the LLM features will be gracefully disabled.
Expand Down
184 changes: 182 additions & 2 deletions web/pgadmin/llm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
MODULE_NAME = 'llm'

# Valid LLM providers
LLM_PROVIDERS = ['anthropic', 'openai', 'ollama', 'docker']
LLM_PROVIDERS = ['anthropic', 'openai', 'glm', 'ollama', 'docker']


class LLMModule(PgAdminModule):
Expand Down Expand Up @@ -71,6 +71,7 @@ def register_preferences(self):
{'label': gettext('None (Disabled)'), 'value': ''},
{'label': gettext('Anthropic'), 'value': 'anthropic'},
{'label': gettext('OpenAI'), 'value': 'openai'},
{'label': gettext('GLM (Z.ai)'), 'value': 'glm'},
{'label': gettext('Ollama'), 'value': 'ollama'},
{'label': gettext('Docker Model Runner'), 'value': 'docker'},
]
Expand Down Expand Up @@ -238,6 +239,66 @@ def register_preferences(self):
}
)

# GLM Settings
# Get defaults from config
glm_url_default = getattr(config, 'GLM_API_URL', '')
glm_key_file_default = getattr(config, 'GLM_API_KEY_FILE', '')
glm_model_default = getattr(config, 'GLM_API_MODEL', '')

self.glm_api_url = self.preference.register(
'glm', 'glm_api_url',
gettext("API URL"), 'text',
glm_url_default,
category_label=gettext('GLM (Z.ai)'),
help_str=gettext(
'URL for the GLM API endpoint. Leave empty to use '
'the default (https://api.z.ai/api/paas/v4). Use '
'https://api.z.ai/api/coding/paas/v4 for a Coding Plan '
'subscription, or set a custom URL to use another '
'GLM-compatible API provider.'
Comment on lines +253 to +258

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Explain the allowlist requirement for remote custom endpoints.

The default allowlist accepts api.z.ai and loopback hosts only, so a remote custom GLM-compatible URL will be rejected unless an administrator adds its scheme, host, and port to ALLOWED_LLM_API_URLS.

  • web/pgadmin/llm/__init__.py#L253-L258: State this constraint in the preference help text.
  • docs/en_US/preferences.rst#L86-L89: Document the required administrator allowlist configuration for non-Z.ai custom URLs.
📍 Affects 2 files
  • web/pgadmin/llm/__init__.py#L253-L258 (this comment)
  • docs/en_US/preferences.rst#L86-L89
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/pgadmin/llm/__init__.py` around lines 253 - 258, The GLM API URL guidance
must explain that remote custom endpoints require administrator allowlisting.
Update the preference help text in web/pgadmin/llm/__init__.py around the GLM
URL setting to mention ALLOWED_LLM_API_URLS and the required scheme, host, and
port; also document this configuration requirement for non-Z.ai custom URLs in
docs/en_US/preferences.rst around lines 86-89.

)
)

self.glm_api_key_file = self.preference.register(
'glm', 'glm_api_key_file',
gettext("API Key File"), 'text',
glm_key_file_default,
category_label=gettext('GLM (Z.ai)'),
help_str=gettext(
'Path to a file containing your GLM API key. '
'This path must be on the server hosting pgAdmin, '
'e.g. inside the container when using Docker. '
'The file should contain only the API key.'
)
)

glm_model_options = []

self.glm_api_model = self.preference.register(
'glm', 'glm_api_model',
gettext("Model"), 'options',
glm_model_default,
category_label=gettext('GLM (Z.ai)'),
options=glm_model_options,
help_str=gettext(
'The GLM model to use. Models are loaded dynamically '
'from your API key. You can also type a custom model name. '
'A model must be selected; there is no default.'
),
control_props={
'allowClear': True,
'creatable': True,
'tags': True,
'placeholder': gettext('Select or type a model name...'),
'optionsUrl': 'llm.models_glm',
'optionsRefreshUrl': 'llm.refresh_models_glm',
'refreshDepNames': {
'api_url': 'glm_api_url',
'api_key_file': 'glm_api_key_file'
}
}
)

# Ollama Settings
# Get defaults from config
ollama_url_default = getattr(config, 'OLLAMA_API_URL', '')
Expand Down Expand Up @@ -334,10 +395,12 @@ def get_exposed_url_endpoints(self):
return [
'llm.models_anthropic',
'llm.models_openai',
'llm.models_glm',
'llm.models_ollama',
'llm.models_docker',
'llm.refresh_models_anthropic',
'llm.refresh_models_openai',
'llm.refresh_models_glm',
'llm.refresh_models_ollama',
'llm.refresh_models_docker',
'llm.status',
Expand Down Expand Up @@ -378,7 +441,7 @@ def get_llm_status():
"""
from pgadmin.llm.utils import (
is_llm_enabled, is_llm_enabled_system, get_default_provider,
get_anthropic_model, get_openai_model, get_ollama_model,
get_anthropic_model, get_openai_model, get_glm_model, get_ollama_model,
get_docker_model
)

Expand All @@ -388,6 +451,8 @@ def get_llm_status():
model = get_anthropic_model()
elif provider == 'openai':
model = get_openai_model()
elif provider == 'glm':
model = get_glm_model()
elif provider == 'ollama':
model = get_ollama_model()
elif provider == 'docker':
Expand Down Expand Up @@ -617,6 +682,108 @@ def refresh_openai_models():
)


@blueprint.route("/models/glm", methods=["GET"], endpoint='models_glm')
@pga_login_required
def get_glm_models():
"""
Fetch available GLM models.
Returns models from the OpenAI-compatible models endpoint.
"""
from pgadmin.llm.utils import get_glm_api_key, get_glm_api_url

api_key = get_glm_api_key()
api_url = get_glm_api_url()
if not api_key and not api_url:
return make_json_response(
data={'models': [], 'error': 'No API key configured'},
status=200
)

try:
models = _fetch_glm_models(api_key, api_url)
return make_json_response(data={'models': models}, status=200)
except LLMApiError as e:
return make_json_response(
data={'models': [], 'error': str(e)},
status=200
)
except Exception:
return make_json_response(
data={'models': [],
'error': 'Failed to fetch GLM models. '
'Check the API key and URL configuration.'},
status=200
)


@blueprint.route(
"/models/glm/refresh",
methods=["POST"],
endpoint='refresh_models_glm'
)
@pga_login_required
def refresh_glm_models():
"""
Fetch available GLM models using a provided API key file path
and/or custom API URL.
Used by the preferences refresh button to load models before saving.
"""
from pgadmin.llm.utils import (
read_api_key_file, validate_api_key_path, validate_api_url
)

data = request.get_json(force=True, silent=True) or {}
api_key_file = data.get('api_key_file', '')
api_url = data.get('api_url', '')

if api_url and not validate_api_url(api_url):
return make_json_response(
data={'models': [],
'error': 'API URL is not in the allowed list. '
'Contact your administrator to update '
'ALLOWED_LLM_API_URLS in the server '
'configuration.'},
status=200
)

api_key = None
if api_key_file:
safe_path = validate_api_key_path(api_key_file)
if safe_path is None:
return make_json_response(
data={'models': [],
'error': 'API key file path is not permitted. '
'The file must be within your private '
'user storage directory; shared storage '
'and other locations are not allowed for '
'security reasons.'},
status=200
)
api_key = read_api_key_file(safe_path)

if not api_key and not api_url:
return make_json_response(
data={'models': [], 'error': 'No API key or custom URL provided'},
status=200
)

try:
models = _fetch_glm_models(api_key, api_url)
return make_json_response(data={'models': models}, status=200)
except LLMApiError as e:
return make_json_response(
data={'models': [], 'error': str(e)},
status=200
)
except Exception:
return make_json_response(
data={'models': [],
'error': 'Failed to fetch GLM models. '
'Check the API key file and URL.'},
status=200
)


@blueprint.route("/models/ollama", methods=["GET"], endpoint='models_ollama')
@pga_login_required
def get_ollama_models():
Expand Down Expand Up @@ -924,6 +1091,19 @@ def _fetch_openai_models(api_key, api_url=''):
return models


def _fetch_glm_models(api_key, api_url=''):
"""
Fetch models from GLM (Z.ai) API.
Returns a list of model options with label and value.
"""
from pgadmin.llm.providers.glm import DEFAULT_API_BASE_URL

return _fetch_openai_models(
api_key=api_key,
api_url=api_url or DEFAULT_API_BASE_URL
)


def _fetch_ollama_models(api_url):
"""
Fetch models from Ollama API.
Expand Down
Loading