Skip to content

Commit 2f80039

Browse files
dpageclaude
andcommitted
Address additional PR review comments.
- Add caching for LLM model options to avoid repeated API calls when switching between preference categories (fixes slow OpenAI model loading issue). - Add icon to AI Reports panel tab (fa-file-alt). - Clear options cache on successful refresh to ensure fresh data. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ca68a8a commit 2f80039

3 files changed

Lines changed: 29 additions & 1 deletion

File tree

web/pgadmin/llm/static/js/ai_tools.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ define([
404404
handler.docker.openTab({
405405
id: panelId,
406406
title: panelTitle,
407+
icon: 'fa fa-file-alt',
407408
content: (
408409
<AIReport
409410
sid={sid}

web/pgadmin/preferences/static/js/components/PreferencesHelper.jsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ import CloseIcon from '@mui/icons-material/CloseRounded';
2020
import HTMLReactParser from 'html-react-parser/lib/index';
2121
import getApiInstance from '../../../../static/js/api_instance';
2222

23+
// Cache for dynamically loaded options to avoid repeated API calls
24+
const optionsCache = {};
25+
26+
// Clear cache for a specific endpoint (used when refresh button updates options)
27+
export function clearOptionsCache(endpoint) {
28+
if (endpoint) {
29+
delete optionsCache[endpoint];
30+
}
31+
}
2332

2433
export async function reloadPgAdmin() {
2534
const { name: browser } = getBrowser();
@@ -123,13 +132,19 @@ export function prepareSubnodeData(node, subNode, nodeData, preferencesStore) {
123132
const optionsEndpoint = element.controlProps.optionsUrl;
124133
const staticOptions = element.options || [];
125134
element.options = () => {
135+
// Check cache first to avoid repeated API calls
136+
if (optionsCache[optionsEndpoint]) {
137+
return Promise.resolve([...optionsCache[optionsEndpoint], ...staticOptions]);
138+
}
126139
return new Promise((resolve) => {
127140
const api = getApiInstance();
128141
const optionsUrl = url_for(optionsEndpoint);
129142
api.get(optionsUrl)
130143
.then((res) => {
131144
if (res.data?.data?.models) {
132145
const dynamicOptions = res.data.data.models;
146+
// Cache the results
147+
optionsCache[optionsEndpoint] = dynamicOptions;
133148
resolve([...dynamicOptions, ...staticOptions]);
134149
} else {
135150
resolve(staticOptions);
@@ -147,6 +162,10 @@ export function prepareSubnodeData(node, subNode, nodeData, preferencesStore) {
147162
const staticOptions = element.options || [];
148163
// Replace options with a function that fetches from the URL
149164
element.options = () => {
165+
// Check cache first to avoid repeated API calls
166+
if (optionsCache[optionsEndpoint]) {
167+
return Promise.resolve([...optionsCache[optionsEndpoint], ...staticOptions]);
168+
}
150169
return new Promise((resolve) => {
151170
const api = getApiInstance();
152171
// Use url_for to resolve the endpoint to a proper URL
@@ -156,6 +175,8 @@ export function prepareSubnodeData(node, subNode, nodeData, preferencesStore) {
156175
if (res.data?.data?.models) {
157176
// Dynamic models loaded successfully
158177
const dynamicOptions = res.data.data.models;
178+
// Cache the results
179+
optionsCache[optionsEndpoint] = dynamicOptions;
159180
resolve([...dynamicOptions, ...staticOptions]);
160181
} else {
161182
// No models in response, use static options

web/pgadmin/static/js/components/SelectRefresh.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import url_for from 'sources/url_for';
2020
import gettext from 'sources/gettext';
2121
import { SchemaStateContext } from '../SchemaView/SchemaState';
2222
import { usePgAdmin } from '../PgAdminProvider';
23+
import { clearOptionsCache } from '../../../preferences/static/js/components/PreferencesHelper';
2324

2425
const StyledBox = styled(Box)(() => ({
2526
display: 'flex',
@@ -69,6 +70,7 @@ export function SelectRefresh({ required, className, label, helpMessage, testcid
6970
const {
7071
getOptionsOnRefresh,
7172
optionsRefreshUrl,
73+
optionsUrl,
7274
refreshDeps,
7375
...selectControlProps
7476
} = controlProps;
@@ -101,6 +103,10 @@ export function SelectRefresh({ required, className, label, helpMessage, testcid
101103
pgAdmin.Browser.notifier.error(res.data.data.error);
102104
} else if (res.data?.data?.models) {
103105
const models = res.data.data.models;
106+
// Clear the cache so next time preferences opens, it uses the refreshed data
107+
if (optionsUrl) {
108+
clearOptionsCache(optionsUrl);
109+
}
104110
setOptionsState((prev) => ({ options: models, reloadBasis: prev.reloadBasis + 1 }));
105111
} else {
106112
// No models returned - clear the list
@@ -132,7 +138,7 @@ export function SelectRefresh({ required, className, label, helpMessage, testcid
132138
setIsRefreshing(false);
133139
});
134140
}
135-
}, [optionsRefreshUrl, refreshDeps, schemaState, getOptionsOnRefresh, pgAdmin]);
141+
}, [optionsRefreshUrl, optionsUrl, refreshDeps, schemaState, getOptionsOnRefresh, pgAdmin]);
136142

137143
return (
138144
<FormInput required={required} label={label} className={className} helpMessage={helpMessage} testcid={testcid}>

0 commit comments

Comments
 (0)