Skip to content

Commit 585f1b4

Browse files
Elaina-Leeclaude
andauthored
feat(standard): Hide Code Interpreter toggle in Standard agent loop behind FF (#8894)
* hide behind ff * test: Add unit tests for Code Interpreter Standard feature flag and manifest Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 89a4f42 commit 585f1b4

File tree

5 files changed

+161
-2
lines changed

5 files changed

+161
-2
lines changed

libs/designer-v2/src/lib/core/utils/experimentation.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { enableAPIMGatewayConnection, enableCodeInterpreterConsumption } from '@microsoft/logic-apps-shared';
1+
import { enableAPIMGatewayConnection, enableCodeInterpreterConsumption, enableCodeInterpreterStandard } from '@microsoft/logic-apps-shared';
22
import { useEffect, useState } from 'react';
33

44
export function useShouldEnableAPIMGatewayConnection(): boolean | null {
@@ -28,3 +28,17 @@ export function useShouldEnableCodeInterpreterConsumption(): boolean | null {
2828

2929
return enabled;
3030
}
31+
32+
export function useShouldEnableCodeInterpreterStandard(): boolean | null {
33+
const [enabled, setEnabled] = useState<boolean | null>(null);
34+
35+
useEffect(() => {
36+
const check = async () => {
37+
const result = await enableCodeInterpreterStandard();
38+
setEnabled(result);
39+
};
40+
check();
41+
}, []);
42+
43+
return enabled;
44+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { describe, vi, beforeEach, it, expect } from 'vitest';
2+
import { InitExperimentationServiceService } from '../experimentation';
3+
import {
4+
enableAPIMGatewayConnection,
5+
enableCodeInterpreterConsumption,
6+
enableCodeInterpreterStandard,
7+
EXP_FLAGS,
8+
} from '../experimentationFlags';
9+
10+
describe('lib/designer-client-services/experimentationFlags', () => {
11+
let mockIsFeatureEnabled: ReturnType<typeof vi.fn>;
12+
13+
beforeEach(() => {
14+
mockIsFeatureEnabled = vi.fn();
15+
InitExperimentationServiceService({
16+
isFeatureEnabled: mockIsFeatureEnabled,
17+
getFeatureValue: vi.fn(),
18+
});
19+
});
20+
21+
describe('enableAPIMGatewayConnection', () => {
22+
it('should return true when feature flag is enabled', async () => {
23+
mockIsFeatureEnabled.mockResolvedValue(true);
24+
const result = await enableAPIMGatewayConnection();
25+
expect(result).toBe(true);
26+
expect(mockIsFeatureEnabled).toHaveBeenCalledWith(EXP_FLAGS.ENABLE_APIM_GEN_AI_GATEWAY);
27+
});
28+
29+
it('should return false when feature flag is disabled', async () => {
30+
mockIsFeatureEnabled.mockResolvedValue(false);
31+
const result = await enableAPIMGatewayConnection();
32+
expect(result).toBe(false);
33+
});
34+
});
35+
36+
describe('enableCodeInterpreterConsumption', () => {
37+
it('should return true when feature flag is enabled', async () => {
38+
mockIsFeatureEnabled.mockResolvedValue(true);
39+
const result = await enableCodeInterpreterConsumption();
40+
expect(result).toBe(true);
41+
expect(mockIsFeatureEnabled).toHaveBeenCalledWith(EXP_FLAGS.ENABLE_CODE_INTERPRETER_CONSUMPTION);
42+
});
43+
44+
it('should return false when feature flag is disabled', async () => {
45+
mockIsFeatureEnabled.mockResolvedValue(false);
46+
const result = await enableCodeInterpreterConsumption();
47+
expect(result).toBe(false);
48+
});
49+
});
50+
51+
describe('enableCodeInterpreterStandard', () => {
52+
it('should return true when feature flag is enabled', async () => {
53+
mockIsFeatureEnabled.mockResolvedValue(true);
54+
const result = await enableCodeInterpreterStandard();
55+
expect(result).toBe(true);
56+
expect(mockIsFeatureEnabled).toHaveBeenCalledWith(EXP_FLAGS.ENABLE_CODE_INTERPRETER_STANDARD);
57+
});
58+
59+
it('should return false when feature flag is disabled', async () => {
60+
mockIsFeatureEnabled.mockResolvedValue(false);
61+
const result = await enableCodeInterpreterStandard();
62+
expect(result).toBe(false);
63+
});
64+
});
65+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { describe, vi, beforeEach, it, expect } from 'vitest';
2+
import { InitExperimentationServiceService } from '../experimentation';
3+
import { StandardOperationManifestService } from '../standard/operationmanifest';
4+
import { agentType, supportedBaseManifestObjects } from '../base/operationmanifest';
5+
6+
describe('StandardOperationManifestService', () => {
7+
let mockIsFeatureEnabled: ReturnType<typeof vi.fn>;
8+
let service: StandardOperationManifestService;
9+
10+
beforeEach(() => {
11+
mockIsFeatureEnabled = vi.fn();
12+
InitExperimentationServiceService({
13+
isFeatureEnabled: mockIsFeatureEnabled,
14+
getFeatureValue: vi.fn(),
15+
});
16+
service = new StandardOperationManifestService({
17+
apiVersion: '2024-02-01',
18+
baseUrl: 'https://test.azure.com',
19+
httpClient: { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() } as any,
20+
});
21+
});
22+
23+
describe('getOperationManifest for agent type', () => {
24+
it('should return manifest without builtinTools when code interpreter flag is disabled', async () => {
25+
mockIsFeatureEnabled.mockResolvedValue(false);
26+
27+
const manifest = await service.getOperationManifest('connectorId', agentType);
28+
29+
const agentChatCompletionSettings = (manifest.properties?.inputs?.properties as any)?.agentModelSettings?.properties
30+
?.agentChatCompletionSettings?.properties;
31+
32+
expect(agentChatCompletionSettings?.builtinTools).toBeUndefined();
33+
});
34+
35+
it('should return manifest with builtinTools when code interpreter flag is enabled', async () => {
36+
mockIsFeatureEnabled.mockResolvedValue(true);
37+
38+
const manifest = await service.getOperationManifest('connectorId', agentType);
39+
40+
const agentChatCompletionSettings = (manifest.properties?.inputs?.properties as any)?.agentModelSettings?.properties
41+
?.agentChatCompletionSettings?.properties;
42+
43+
expect(agentChatCompletionSettings?.builtinTools).toBeDefined();
44+
});
45+
46+
it('should not mutate the original manifest when stripping builtinTools', async () => {
47+
mockIsFeatureEnabled.mockResolvedValue(false);
48+
49+
await service.getOperationManifest('connectorId', agentType);
50+
51+
// Verify the original manifest in supportedBaseManifestObjects is untouched
52+
const originalManifest = supportedBaseManifestObjects.get(agentType);
53+
const originalBuiltinTools = (originalManifest?.properties?.inputs?.properties as any)?.agentModelSettings?.properties
54+
?.agentChatCompletionSettings?.properties?.builtinTools;
55+
56+
expect(originalBuiltinTools).toBeDefined();
57+
});
58+
});
59+
});

libs/logic-apps-shared/src/designer-client-services/lib/experimentationFlags.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ExperimentationService } from './experimentation';
33
export const EXP_FLAGS = {
44
ENABLE_APIM_GEN_AI_GATEWAY: 'enable-apim-gen-ai-gateway',
55
ENABLE_CODE_INTERPRETER_CONSUMPTION: 'enable-code-interpreter-consumption',
6+
ENABLE_CODE_INTERPRETER_STANDARD: 'enable-code-interpreter-standard',
67
};
78

89
export async function enableAPIMGatewayConnection(): Promise<boolean> {
@@ -12,3 +13,7 @@ export async function enableAPIMGatewayConnection(): Promise<boolean> {
1213
export async function enableCodeInterpreterConsumption(): Promise<boolean> {
1314
return ExperimentationService().isFeatureEnabled(EXP_FLAGS.ENABLE_CODE_INTERPRETER_CONSUMPTION);
1415
}
16+
17+
export async function enableCodeInterpreterStandard(): Promise<boolean> {
18+
return ExperimentationService().isFeatureEnabled(EXP_FLAGS.ENABLE_CODE_INTERPRETER_STANDARD);
19+
}

libs/logic-apps-shared/src/designer-client-services/lib/standard/operationmanifest.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import type { Connector, OperationInfo, OperationManifest } from '../../../utils/src';
22
import { ConnectionType, equals } from '../../../utils/src';
3+
import { enableCodeInterpreterStandard } from '../experimentationFlags';
34
import { BaseOperationManifestService } from '../base';
45
import type { BaseOperationManifestServiceOptions } from '../base/operationmanifest';
5-
import { getBuiltInOperationInfo, isBuiltInOperation, mcpclientConnectorId, supportedBaseManifestObjects } from '../base/operationmanifest';
6+
import {
7+
agentType,
8+
getBuiltInOperationInfo,
9+
isBuiltInOperation,
10+
mcpclientConnectorId,
11+
supportedBaseManifestObjects,
12+
} from '../base/operationmanifest';
613
import { getHybridAppBaseRelativeUrl, hybridApiVersion, isHybridLogicApp } from './hybrid';
714
import { getClientBuiltInConnectors } from '../base/search';
815
import { aiOperationsGroup } from './operations/operationgroups';
@@ -154,6 +161,15 @@ export class StandardOperationManifestService extends BaseOperationManifestServi
154161
override async getOperationManifest(connectorId: string, operationId: string): Promise<OperationManifest> {
155162
const supportedManifest = supportedBaseManifestObjects.get(operationId);
156163
if (supportedManifest) {
164+
if (operationId === agentType) {
165+
const isCodeInterpreterEnabled = await enableCodeInterpreterStandard();
166+
if (!isCodeInterpreterEnabled) {
167+
const manifest: OperationManifest = JSON.parse(JSON.stringify(supportedManifest));
168+
delete (manifest.properties?.inputs?.properties as any)?.agentModelSettings?.properties?.agentChatCompletionSettings?.properties
169+
?.builtinTools;
170+
return manifest;
171+
}
172+
}
157173
return supportedManifest;
158174
}
159175

0 commit comments

Comments
 (0)