fix(task-execution): resolve bias badge from catalog

This commit is contained in:
Lucio Lelii 2026-07-23 11:09:58 +02:00
parent cc1f773c2d
commit f633493bd6
3 changed files with 53 additions and 5 deletions

View File

@ -87,8 +87,8 @@
{{ activeBiasAnnotationCount() }}/{{ allBiasAnnotations().length }}
</span>
}
@if (biasCapabilities?.supported) {
<span class="llm-node-bias-capability" title="Bias impact capabilities available">
@if (isBiasCapable()) {
<span class="llm-node-bias-capability" title="Bias annotations supported by this node type">
Bias capable
</span>
}

View File

@ -120,10 +120,49 @@ describe('TaskStepNodeComponent bias canvas highlighting', () => {
expect(component.allBiasAnnotations()).toHaveLength(2);
expect(component.activeBiasAnnotationCount()).toBe(1);
expect(component.capabilitiesTooltip()).toContain('Bias annotations: allowed');
expect(component.isBiasCapable()).toBe(true);
});
it('does not mark EndBlock as bias capable when the catalog forbids bias annotations', () => {
component.data.data.typeName = 'EndBlock';
component.data.data.capabilities = undefined;
(component as any).blockDescriptor = {
capabilities: {
visualRole: 'END',
terminal: true,
biasAnnotationsAllowed: false,
allowsIncomingConnections: true,
allowsOutgoingConnections: false,
canDependOnOtherNodes: true,
canHaveDependentNodes: false
}
};
fixture.detectChanges();
expect(component.isBiasCapable()).toBe(false);
expect(fixture.nativeElement.querySelector('.llm-node-bias-capability')).toBeNull();
});
it('waits for catalog capabilities instead of using permissive defaults for the badge', () => {
component.data.data.capabilities = undefined;
(component as any).blockDescriptor = null;
fixture.detectChanges();
expect(component.isBiasCapable()).toBe(false);
expect(fixture.nativeElement.querySelector('.llm-node-bias-capability')).toBeNull();
});
describe('Measure bias impact availability', () => {
beforeEach(() => {
component.data.data.capabilities = {
visualRole: 'PROCESS',
terminal: false,
biasAnnotationsAllowed: true,
allowsIncomingConnections: true,
allowsOutgoingConnections: true,
canDependOnOtherNodes: true,
canHaveDependentNodes: true
};
component.data.data.biasAnnotations = [
{ id: 'annotation-1', behavioralProbe: { activationMode: 'PROMPT_DIRECTIVE', instruction: 'Nudge it' } }
];

View File

@ -451,11 +451,14 @@ export class TaskStepNodeComponent {
}
typeCapabilities(): NodeTypeCapabilities {
return this.data?.data?.capabilities
?? this.blockDescriptor?.capabilities
return this.resolvedTypeCapabilities()
?? DEFAULT_NODE_CAPABILITIES;
}
isBiasCapable(): boolean {
return this.resolvedTypeCapabilities()?.biasAnnotationsAllowed === true;
}
visualRoleLabel(): string {
const role = this.typeCapabilities().visualRole.toLowerCase();
return role.charAt(0).toUpperCase() + role.slice(1);
@ -474,7 +477,7 @@ export class TaskStepNodeComponent {
}
hasMeasurableBiasAnnotations(): boolean {
return this.typeCapabilities().biasAnnotationsAllowed
return this.isBiasCapable()
&& this.executableBiasAnnotations().length > 0
&& this.biasCapabilities?.isolatedExperimentSupported === true;
}
@ -610,6 +613,12 @@ export class TaskStepNodeComponent {
return typeof typeName === 'string' && typeName.length > 0 ? typeName : null;
}
private resolvedTypeCapabilities(): NodeTypeCapabilities | null {
return this.data?.data?.capabilities
?? this.blockDescriptor?.capabilities
?? null;
}
private actionDescriptionValue(): string {
const value = this.blockConfiguration?.['actionDescription'];
if (typeof value === 'string') return value;