fix: VMware Tanzu Platform provider - bug fixes, streaming, UI improvements (#8126)

Signed-off-by: Nick Kuhn <nick.kuhn@broadcom.com>
Signed-off-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Nick Kuhn
2026-03-26 14:16:01 -04:00
committed by GitHub
parent cdf91ea798
commit c936514014
16 changed files with 725 additions and 61 deletions
+5
View File
@@ -331,6 +331,11 @@ export type EnvVarConfig = {
default?: string | null;
description?: string | null;
name: string;
/**
* When true, the field is shown prominently in the UI (not collapsed).
* Defaults to the value of `required` if not specified.
*/
primary?: boolean | null;
required?: boolean;
secret?: boolean;
};
@@ -8,6 +8,7 @@ import OpenRouterLogo from './icons/openrouter@3x.png';
import SnowflakeLogo from './icons/snowflake@3x.png';
import XaiLogo from './icons/xai@3x.png';
import MiniMaxLogo from './icons/minimax@3x.png';
import TanzuLogo from './icons/tanzu@3x.png';
import DefaultLogo from './icons/default@3x.png';
// Map provider names to their logos
@@ -22,6 +23,7 @@ const providerLogos: Record<string, string> = {
snowflake: SnowflakeLogo,
xai: XaiLogo,
minimax: MiniMaxLogo,
tanzu_ai: TanzuLogo,
default: DefaultLogo,
};
@@ -60,7 +60,7 @@ export default function DefaultProviderSetupForm({
const configKey = `${parameter.name}`;
const configValue = (await read(configKey, parameter.secret || false)) as ConfigValue;
if (configValue) {
if (configValue !== undefined && configValue !== null) {
values[parameter.name] = { serverValue: configValue };
} else if (parameter.default !== undefined && parameter.default !== null) {
values[parameter.name] = { value: parameter.default };
@@ -127,47 +127,109 @@ export default function DefaultProviderSetupForm({
return <div className="text-center py-4">Loading configuration values...</div>;
}
function getRenderValue(parameter: ConfigKey): string | undefined {
if (parameter.secret) {
return undefined;
}
function getRenderValue(parameter: ConfigKey): string {
const entry = configValues[parameter.name];
return entry?.value || (entry?.serverValue as string) || '';
// If the user has edited the field (even to empty string), use their value.
// This prevents the input from snapping back to the stored serverValue
// when the user backspaces to clear the field.
if (entry?.value !== undefined) {
return entry.value;
}
if (parameter.secret) {
return '';
}
// Convert serverValue to string explicitly — native booleans (false) would
// be falsy and get collapsed to '' by the || operator, losing the value.
if (entry?.serverValue !== undefined && entry?.serverValue !== null) {
return String(entry.serverValue);
}
return '';
}
// Detect boolean parameters (default is "true" or "false")
function isBooleanParameter(parameter: ConfigKey): boolean {
const def = parameter.default?.toLowerCase();
return def === 'true' || def === 'false';
}
function getBooleanValue(parameter: ConfigKey): boolean {
const raw = getRenderValue(parameter);
const val = String(raw).toLowerCase();
if (val === '' && parameter.default) {
return parameter.default.toLowerCase() === 'true';
}
return val === 'true';
}
// Pretty label for boolean toggle (strip provider prefix, humanize)
function getBooleanLabel(parameter: ConfigKey): string {
let name = parameter.name.toUpperCase();
const prefix = provider.name.toUpperCase().replace('-', '_') + '_';
if (name.startsWith(prefix)) {
name = name.slice(prefix.length);
}
return envToPrettyName(name);
}
const renderParametersList = (parameters: ConfigKey[]) => {
return parameters.map((parameter) => (
<div key={parameter.name}>
<label className="block text-sm font-medium text-text-primary mb-1">
{getFieldLabel(parameter)}
{parameter.required && <span className="text-red-500 ml-1">*</span>}
</label>
<Input
type="text"
value={getRenderValue(parameter)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setConfigValues((prev) => {
const newValue = { ...(prev[parameter.name] || {}), value: e.target.value };
return {
...prev,
[parameter.name]: newValue,
};
});
}}
placeholder={getPlaceholder(parameter)}
className={`w-full h-14 px-4 font-regular rounded-lg shadow-none ${
validationErrors[parameter.name]
? 'border-2 border-red-500'
: 'border border-border-primary hover:border-border-primary'
} bg-background-primary text-lg placeholder:text-text-secondary font-regular text-text-primary`}
required={parameter.required}
/>
{validationErrors[parameter.name] && (
<p className="text-red-500 text-sm mt-1">{validationErrors[parameter.name]}</p>
)}
</div>
));
return parameters.map((parameter) => {
if (isBooleanParameter(parameter)) {
return (
<div key={parameter.name} className="flex items-center space-x-2 py-2">
<input
type="checkbox"
id={`toggle-${parameter.name}`}
checked={getBooleanValue(parameter)}
onChange={(e) => {
setConfigValues((prev) => ({
...prev,
[parameter.name]: {
...(prev[parameter.name] || {}),
value: e.target.checked ? 'true' : 'false',
},
}));
}}
className="rounded border-border-primary h-4 w-4"
/>
<label htmlFor={`toggle-${parameter.name}`} className="text-sm text-text-secondary">
{getBooleanLabel(parameter)}
</label>
</div>
);
}
return (
<div key={parameter.name}>
<label className="block text-sm font-medium text-text-primary mb-1">
{getFieldLabel(parameter)}
{parameter.required && <span className="text-red-500 ml-1">*</span>}
</label>
<Input
type="text"
value={getRenderValue(parameter)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setConfigValues((prev) => {
const newValue = { ...(prev[parameter.name] || {}), value: e.target.value };
return {
...prev,
[parameter.name]: newValue,
};
});
}}
placeholder={getPlaceholder(parameter)}
className={`w-full h-14 px-4 font-regular rounded-lg shadow-none ${
validationErrors[parameter.name]
? 'border-2 border-red-500'
: 'border border-border-primary hover:border-border-primary'
} bg-background-primary text-lg placeholder:text-text-secondary font-regular text-text-primary`}
required={parameter.required}
/>
{validationErrors[parameter.name] && (
<p className="text-red-500 text-sm mt-1">{validationErrors[parameter.name]}</p>
)}
</div>
);
});
};
let aboveFoldParameters = parameters.filter(
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB