Skip to content
Merged
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
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.261.314"
VERSION = "0.261.315"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
4 changes: 3 additions & 1 deletion application/single_app/route_backend_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from functions_model_capabilities import ModelTokenBudgetError
from functions_model_endpoint_app_identity import check_application_identity_request, check_application_identity_save
from functions_model_endpoint_runtime import build_model_endpoint_sync_chat_client
from functions_model_endpoint_providers import get_model_endpoint_provider_ui_options

Check warning on line 31 in application/single_app/route_backend_models.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
from functions_model_endpoint_types import (
DEFAULT_ANTHROPIC_VERSION,
MODEL_ENDPOINT_PROVIDER_CUSTOM,
Expand Down Expand Up @@ -1169,7 +1170,8 @@
user_settings = get_user_settings(user_id)
endpoints = user_settings.get("settings", {}).get("personal_model_endpoints", [])
return jsonify({
"endpoints": sanitize_model_endpoints_for_frontend(endpoints)
"endpoints": sanitize_model_endpoints_for_frontend(endpoints),

Check warning on line 1173 in application/single_app/route_backend_models.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
"custom_api_types": get_model_endpoint_provider_ui_options(),

Check warning on line 1174 in application/single_app/route_backend_models.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
})


Expand Down
57 changes: 44 additions & 13 deletions application/v2_ui/src/components/admin/ModelConnectionsManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
PROVIDER_OPTIONS,
authTypeLabel,
buildConnectionPayload,
buildPersonalConnectionPayload,
createAdminModelConnectionsAdapter,
defaultOpenAiApiVersion,
defaultEmbeddingApi,
Expand All @@ -59,10 +60,12 @@
modelSupportsCapability,
projectNameFromEndpoint,
providerLabel,
personalTestBlockedReason,
setModelCapabilityEnabled,
setEmbeddingOperation,
toEditableConnection,
validateConnection,
validateAdvancedConnection,

Check warning on line 68 in application/v2_ui/src/components/admin/ModelConnectionsManager.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
visibleFields,
ENDPOINT_REBASE_FIELDS,
type ConnectionModel,
Expand All @@ -80,6 +83,7 @@
import { CustomAuthenticationFields, CustomConnectionFields } from './CustomConnectionFields';
import { CustomNetworkPolicyEditor } from './CustomNetworkPolicyEditor';
import { GlassButton } from '../ui/primitives';
import { ConnectionCapacityFields, ConnectionModelMetadata } from '../workspace/ConnectionMetadataFields';
import { useModelConnectionsStore, modelConnectionsChanged } from '../../stores/modelConnectionsStore';
import { toast } from '../../stores/toastStore';

Expand Down Expand Up @@ -388,6 +392,7 @@
const [discovering, setDiscovering] = useState(false);
const [testing, setTesting] = useState(false);
const [testingModelId, setTestingModelId] = useState<string | null>(null);
const [iconBusy, setIconBusy] = useState(false);
const [formError, setFormError] = useState<string | null>(null);
// A stale-revision conflict keeps the draft and offers a reload rather than losing the edit.
const [needsReload, setNeedsReload] = useState(false);
Expand Down Expand Up @@ -416,8 +421,11 @@
const canSave = isNew ? adapter.canCreate : adapter.allows('edit', initial);

const shown = useMemo(() => visibleFields(draft), [draft]);
const savedBinding = !isNew && JSON.stringify(buildConnectionPayload(draft)) ===
JSON.stringify(buildConnectionPayload(toEditableConnection(current)));
const testBlockedReason = adapter.requiresSavedConfigurationTests ? personalTestBlockedReason(draft, current) : null;
const savedBinding = !isNew && (adapter.requiresSavedConfigurationTests ? !testBlockedReason
: JSON.stringify(buildConnectionPayload(draft)) === JSON.stringify(buildConnectionPayload(toEditableConnection(current))));
const editorPayload = (value: ModelConnection) => adapter.advancedEditing
? buildPersonalConnectionPayload(value) : buildConnectionPayload(value);

const setField = useCallback((path: string, value: unknown) => {
setErrors((current) => {
Expand All @@ -430,7 +438,7 @@
});
setDraft((current) => {
const next = { ...current };
if (path === 'name' || path === 'provider' || path === 'enabled' || path === 'api_type') {
if (!path.includes('.')) {
(next as Record<string, unknown>)[path] = value;
// Switching provider changes which API version default applies, and the
// previous provider's default would otherwise be silently carried over.
Expand Down Expand Up @@ -481,6 +489,7 @@
};

const runDiscovery = async () => {
if (testBlockedReason) { setFormError(testBlockedReason); return; }
const validation = validateConnection(draft, { requireDiscovery: true });
// Discovery needs the connection and credentials, but not the model list, so a
// missing name should not stop it.
Expand All @@ -494,7 +503,7 @@
setDiscovering(true);
setFormError(null);
try {
const response = await adapter.discover(buildConnectionPayload(draft));
const response = await adapter.discover(editorPayload(draft));
const discovered = Array.isArray(response.models) ? response.models : [];
const { models, added } = mergeDiscoveredModels(draft.models ?? [], discovered);
setModels(models);
Expand Down Expand Up @@ -539,6 +548,11 @@
};

const runModelTest = async (model: ConnectionModel, capability: ImplementedCapability) => {
if (testBlockedReason) { setFormError(testBlockedReason); return; }
if (adapter.requiresSavedConfigurationTests && model.enabled === false) {
setFormError('Enable the model and save the endpoint before testing it.');
return;
}
if (embeddingOnly && capability !== 'embeddings') return;
if (capability !== 'chat' && !savedBinding) {
setFormError(`Save the connection first. ${capability === 'embeddings' ? 'Embedding' : 'Image'} tests use only the saved model and credentials.`);
Expand Down Expand Up @@ -574,7 +588,7 @@
toast.success(`${deploymentName} generated an image.`);
}
} else {
await adapter.testConnectionModel(buildConnectionPayload(draft), model);
await adapter.testConnectionModel(editorPayload(draft), model);
toast.success(`${deploymentName} answered a chat request. Image inference was not tested.`);
}
} catch (error) {
Expand All @@ -585,7 +599,7 @@
};

const save = async () => {
const validation = validateConnection(draft);
const validation = { ...validateConnection(draft), ...(adapter.advancedEditing ? validateAdvancedConnection(draft) : {}) };

Check warning on line 602 in application/v2_ui/src/components/admin/ModelConnectionsManager.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
if (Object.keys(validation).length) {
setErrors(validation);
setFormError('Some details still need attention.');
Expand All @@ -596,7 +610,7 @@
setFormError(null);
setNeedsReload(false);
try {
const payload = buildConnectionPayload(draft);
const payload = editorPayload(draft);
const response = initial.id
? await adapter.update(current, payload)
: await adapter.create(payload);
Expand Down Expand Up @@ -643,7 +657,7 @@
}
};

const busy = saving || discovering || testing || reloading || testingModelId !== null;
const busy = saving || discovering || testing || reloading || testingModelId !== null || iconBusy;
const models = draft.models ?? [];
const projectHint = foundry ? projectNameFromEndpoint(draft.connection?.endpoint) : '';

Expand Down Expand Up @@ -700,6 +714,7 @@
) : null}

<fieldset disabled={!canSave} className="min-w-0">
{adapter.advancedEditing ? <link rel="stylesheet" href="/static/css/bootstrap-icons.css" /> : null}
<SectionHeading>Identity</SectionHeading>

<Field label="Name" error={errors.name} htmlFor="connection-name" help="Shown wherever a model from this connection is offered.">
Expand Down Expand Up @@ -1149,7 +1164,7 @@
variant="subtle"
size="sm"
onClick={() => void runDiscovery()}
disabled={busy || !shown.discovery}
disabled={busy || !shown.discovery || Boolean(testBlockedReason)}

Check warning on line 1167 in application/v2_ui/src/components/admin/ModelConnectionsManager.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
>
{discovering ? (
<Loader2 size={14} className="animate-spin" />
Expand Down Expand Up @@ -1194,10 +1209,14 @@
Add manually
</GlassButton>
</div>
<p className="mb-3 text-xs text-text-3">
{adapter.scope.kind === 'personal' ? <p className="mb-3 text-xs text-text-3" role="status">

Check warning on line 1212 in application/v2_ui/src/components/admin/ModelConnectionsManager.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
{testBlockedReason || 'Chat tests may incur inference costs. Saving configuration is not an inference test. Image and embedding tests are available only for administrator-managed connections.'}

Check warning on line 1213 in application/v2_ui/src/components/admin/ModelConnectionsManager.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
</p> : <p className="mb-3 text-xs text-text-3">
Connection checks do not test image or embedding inference. Operation tests use saved bindings and may incur inference costs.
{!savedBinding ? ' Save the connection first before testing images or embeddings, or choosing a default.' : ''}
</p>
</p>}
{adapter.advancedEditing ? <ConnectionCapacityFields record={draft} disabled={busy} errors={errors}

Check warning on line 1218 in application/v2_ui/src/components/admin/ModelConnectionsManager.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
onChange={(key, value) => setField(key, value)} /> : null}
{errors.models ? <p role="alert" className="mb-2 text-xs text-danger">{errors.models}</p> : null}

{models.length === 0 ? (
Expand Down Expand Up @@ -1327,9 +1346,17 @@
.map(([name, message]) => [name.slice(`model_${index}_`.length), message]))}
onChange={(nextModel) => setModels(models.map((item, at) => at === index ? nextModel : item))}
/>
{adapter.advancedEditing ? <ConnectionModelMetadata
model={model} disabled={busy}

Check warning on line 1350 in application/v2_ui/src/components/admin/ModelConnectionsManager.tsx

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
errors={Object.fromEntries(Object.entries(errors)
.filter(([name]) => name.startsWith(`model_${index}_`))
.map(([name, message]) => [name.slice(`model_${index}_`.length), message]))}
onChange={(nextModel) => setModels(models.map((item, at) => at === index ? nextModel : item))}
onIconBusyChange={setIconBusy}
/> : null}
<div className="mt-3 flex flex-wrap gap-2">
{canTest && !embeddingOnly && modelPublishesCapability(model, 'chat') ? (
<GlassButton type="button" variant="subtle" size="sm" disabled={busy} onClick={() => void runModelTest(model, 'chat')}>
<GlassButton type="button" variant="subtle" size="sm" disabled={busy || Boolean(testBlockedReason)} onClick={() => void runModelTest(model, 'chat')}>
Test chat
</GlassButton>
) : null}
Expand Down Expand Up @@ -1632,7 +1659,11 @@
</div>

{help ? <p className="mb-3 text-xs leading-relaxed text-text-3">{help}</p> : null}
<p className="mb-3 text-xs text-text-3">Configure credentials once, then choose independent chat, image and embedding defaults. Images and embeddings remain available when chat uses its classic endpoint.</p>
<p className="mb-3 text-xs text-text-3">
{adapter.scope.kind === 'personal'
? 'Save each connection individually and enable the models your agents and workflows should use. Personal connections do not change global model defaults.'
: 'Configure credentials once, then choose independent chat, image and embedding defaults. Images and embeddings remain available when chat uses its classic endpoint.'}
</p>
{adapter.canEditNetworkPolicy && !loading ? <CustomNetworkPolicyEditor policy={customNetworkPolicy} onSaved={setCustomNetworkPolicy} /> : null}
{adapter.showMigrationNotices ? [migration, embeddingMigration].map((notice, index) => notice?.message ? (
<p key={index} role="status" className={`mb-3 rounded-lg p-3 text-xs ${notice.status === 'complete' ? 'bg-surface-2 text-text-2' : 'bg-warn-soft text-warn'}`}>
Expand Down
Loading
Loading