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
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,10 @@ orchestration. The `Details in a dialog` and `Details in a panel overlay` storie
(SSN)", not `USSocialSecurityNumber`) and each validator names its own configuration
("Severity thresholds" for harmful content, "Detection thresholds" for PII). With no
definition matched it falls back to generic labels and raw values, which is what both
products render today.
products render today. A built-in's own parameters the definition does not declare take the
curated copy for the validator ("Rule prompt", "Languages"), and `appliesTo`, which Agents
stamps onto every built-in it enforces on files, the details' own "Applies to" / "Files
only" fallback, so the dialog reads as the per-agent builder does.
Comment on lines +603 to +606
- **A read-only value is text, not a disabled input.** The family's parameter editors are the
MetadataForm stack and have no read-only mode, and these values arrive as untyped wire data
rather than `GuardrailValidatorParameter`s. A disabled input, which is how Flow renders this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { render, screen, within } from '@testing-library/react';
import { axe } from 'jest-axe';
import { describe, expect, it } from 'vitest';
import { ApI18nProvider } from '../../../i18n';
import { SENTIMENT_WIRE } from './__fixtures__/definitions-wire.fixtures';
import {
LLM_AS_JUDGE_WIRE,
PII_DETECTION_WIRE,
SENTIMENT_WIRE,
} from './__fixtures__/definitions-wire.fixtures';
import { CentralizedGuardrailDetails } from './centralized-guardrail-details';
import type { CentralizedGuardrail, CentralizedGuardrailDefinition } from './centralized-types';
import { enrichGuardrailDefinitions } from './definitions-enrich';
Expand Down Expand Up @@ -179,6 +183,86 @@ describe('CentralizedGuardrailDetails', () => {
).toEqual(['Negative0.7', 'Mixed—']);
});

it('shows which content a built-in applies to, under its thresholds', () => {
render(
<CentralizedGuardrailDetails
guardrail={guardrail({
entities: ['Email'],
entityThresholds: { Email: 0.8 },
parameters: [{ id: 'appliesTo', parameterType: 'enum', value: 'Files' }],
})}
definitions={enrichGuardrailDefinitions([PII_DETECTION_WIRE])}
policyName="Acme policy"
/>
);

const configuration = screen.getByText('Configuration').parentElement as HTMLElement;
const terms = within(configuration).getAllByRole('term');
expect(terms.map((term) => term.textContent)).toEqual(['Detection thresholds', 'Applies to']);
expect(valueFor('Applies to')).toBe('Files only');
});

it('names the judge’s settings the way its editor does, with no definition at all', () => {
render(
<CentralizedGuardrailDetails
guardrail={guardrail({
validator: 'llm_as_judge',
parameters: [
{ id: 'guardrailText', parameterType: 'text', value: 'No medical advice.' },
{ id: 'model', parameterType: 'enum', value: 'gpt-4o-2024-11-20' },
{ id: 'threshold', parameterType: 'number', value: 4 },
{ id: 'positiveExamples', parameterType: 'text-list', value: ['Take an aspirin.'] },
{ id: 'negativeExamples', parameterType: 'text-list', value: ['See a doctor.'] },
{ id: 'appliesTo', parameterType: 'enum', value: 'Both' },
],
})}
definitions={[]}
policyName="Acme policy"
/>
);

const configuration = screen.getByText('Configuration').parentElement as HTMLElement;
expect(
within(configuration)
.getAllByRole('term')
.map((term) => term.textContent)
).toEqual([
'Rule prompt',
'Judge model',
'Strictness',
'Positive examples',
'Negative examples',
'Applies to',
]);
expect(valueFor('Strictness')).toBe('4');
expect(valueFor('Applies to')).toBe('Text and files');
});

it('follows the definition’s order for the judge and appends what it does not declare', () => {
render(
<CentralizedGuardrailDetails
guardrail={guardrail({
validator: 'llm_as_judge',
parameters: [
{ id: 'appliesTo', parameterType: 'enum', value: 'Text' },
{ id: 'threshold', parameterType: 'number', value: 2 },
{ id: 'guardrailText', parameterType: 'text', value: 'Stay on topic.' },
],
})}
definitions={enrichGuardrailDefinitions([LLM_AS_JUDGE_WIRE])}
policyName="Acme policy"
/>
);

const configuration = screen.getByText('Configuration').parentElement as HTMLElement;
expect(
within(configuration)
.getAllByRole('term')
.map((term) => term.textContent)
).toEqual(['Rule prompt', 'Strictness', 'Applies to']);
expect(valueFor('Applies to')).toBe('Text only');
});

it('renders a BYO connector configuration in the order the connector declares', () => {
render(
<CentralizedGuardrailDetails
Expand Down Expand Up @@ -247,6 +331,7 @@ describe('CentralizedGuardrailDetails', () => {
guardrail={guardrail({
entities: ['Email'],
entityThresholds: { Email: 0.8 },
parameters: [{ id: 'appliesTo', parameterType: 'enum', value: 'Files' }],
})}
definitions={[PII_DEFINITION]}
policyName="Acme policy"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,20 @@ export function CentralizedGuardrailDetails<

const parameterRows = resolveCentralizedGuardrailParameters(guardrail, {
definition,
copy,
labels: {
enabled: labels.parameterEnabled,
disabled: labels.parameterDisabled,
entities: labels.entitiesFallback,
thresholds: labels.thresholdsFallback,
appliesTo: {
label: labels.appliesToFallback,
options: {
Text: labels.appliesToText,
Files: labels.appliesToFiles,
Both: labels.appliesToBoth,
},
},
},
});

Expand Down
Loading
Loading