| // Copyright 2026 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import {assert} from 'chai'; | |
| import {describe, it} from 'mocha'; | |
| import {setupLocaleHooks} from '../../testing/LocaleHelpers.js'; | |
| import {setupRuntimeHooks} from '../../testing/RuntimeHelpers.js'; | |
| import {AidaClient, AidaGcaTranslation, GcaTypes} from './host.js'; | |
| const DEFAULT_CLIENT = 'CHROME_DEVTOOLS'; | |
| const DEFAULT_METADATA: AidaClient.RequestMetadata = { | |
| disable_user_content_logging: false, | |
| client_version: '1.2.3.4', | |
| }; | |
| const DEFAULT_LABELS = { | |
| client: 'chrome_devtools', | |
| client_version: '1_2_3_4', | |
| disable_user_content_logging: 'false', | |
| }; | |
| function createAidaDoConversationRequest(overrides: Partial<AidaClient.DoConversationRequest> = {}): | |
| AidaClient.DoConversationRequest { | |
| const {metadata, ...otherOverrides} = overrides; | |
| return { | |
| current_message: {parts: [{text: 'Hello'}], role: AidaClient.Role.USER}, | |
| client: DEFAULT_CLIENT, | |
| client_feature: AidaClient.ClientFeature.CHROME_CONSOLE_INSIGHTS, | |
| metadata: {...DEFAULT_METADATA, ...metadata}, | |
| ...otherOverrides, | |
| }; | |
| } | |
| function createGcaRequest( | |
| experience: string, overrides: Partial<GcaTypes.GenerateContentRequest> = {}): GcaTypes.GenerateContentRequest { | |
| const {labels, aicode, ...otherOverrides} = overrides; | |
| const defaultFeatureLabel: Record<string, string> = | |
| experience === 'chat_console_insights' ? {client_feature: 'chrome_console_insights'} : {}; | |
| return { | |
| contents: [{role: 'user', parts: [{text: 'Hello'}]}], | |
| ...otherOverrides, | |
| labels: {...DEFAULT_LABELS, ...defaultFeatureLabel, ...labels}, | |
| aicode: aicode || { | |
| experience, | |
| }, | |
| }; | |
| } | |
| function createGcaResponse(overrides: Partial<GcaTypes.GenerateContentResponse> = {}): | |
| GcaTypes.GenerateContentResponse { | |
| return { | |
| candidates: [{ | |
| index: 0, | |
| content: {role: 'model', parts: [{text: 'Hello there!'}]}, | |
| finishReason: GcaTypes.FinishReason.STOP, | |
| safetyRatings: [], | |
| citationMetadata: {citations: []}, | |
| groundingMetadata: {}, | |
| aicodeOutput: {contents: []}, | |
| }], | |
| promptFeedback: | |
| {blockReason: GcaTypes.BlockReason.BLOCKED_REASON_UNSPECIFIED, safetyRatings: [], blockReasonMessage: ''}, | |
| usageMetadata: { | |
| promptTokenCount: 0, | |
| candidatesTokenCount: 0, | |
| totalTokenCount: 0, | |
| thoughtsTokenCount: 0, | |
| cachedContentTokenCount: 0, | |
| }, | |
| modelVersion: 'test-model', | |
| responseId: 'response-123', | |
| ...overrides, | |
| }; | |
| } | |
| function createAidaEvent(overrides: Partial<AidaClient.AidaRegisterClientEvent> = {}): | |
| AidaClient.AidaRegisterClientEvent { | |
| return { | |
| corresponding_aida_rpc_global_id: '123', | |
| disable_user_content_logging: false, | |
| ...overrides, | |
| }; | |
| } | |
| /* eslint-disable @typescript-eslint/no-explicit-any */ | |
| function describeCommonRequestFields( | |
| aidaRequestFactory: (overrides: any) => any, | |
| translateFn: (req: any) => GcaTypes.GenerateContentRequest, | |
| ) { | |
| describe('common fields', () => { | |
| it('maps common options and metadata', () => { | |
| const aidaRequest = aidaRequestFactory({ | |
| options: {temperature: 0.5, model_id: 'test-model'}, | |
| metadata: {string_session_id: 'session-123'}, | |
| }); | |
| const result = translateFn(aidaRequest); | |
| assert.strictEqual(result.model, 'test-model'); | |
| assert.strictEqual(result.labels?.['session_id'], 'session-123'); | |
| assert.strictEqual(result.labels?.['client_version'], '1_2_3_4'); | |
| assert.strictEqual(result.labels?.['disable_user_content_logging'], 'false'); | |
| assert.strictEqual(result.generationConfig?.temperature, 0.5); | |
| }); | |
| it('maps client and feature labels', () => { | |
| const aidaRequest = aidaRequestFactory({ | |
| functionality_type: AidaClient.FunctionalityType.EXPLAIN_ERROR, | |
| client_feature: AidaClient.ClientFeature.CHROME_CONSOLE_INSIGHTS, | |
| }); | |
| const result = translateFn(aidaRequest); | |
| assert.strictEqual(result.labels?.['client'], 'chrome_devtools'); | |
| assert.strictEqual(result.labels?.['functionality_type'], 'explain_error'); | |
| assert.strictEqual(result.labels?.['client_feature'], 'chrome_console_insights'); | |
| }); | |
| }); | |
| } | |
| describe('AidaGcaTranslation', () => { | |
| setupLocaleHooks(); | |
| setupRuntimeHooks(); | |
| describe('AIDA DoConversationRequest to GCA GenerateContentRequest', () => { | |
| describeCommonRequestFields( | |
| createAidaDoConversationRequest, AidaGcaTranslation.aidaDoConversationRequestToGcaRequest); | |
| it('translates a basic request', () => { | |
| assert.deepEqual( | |
| AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(createAidaDoConversationRequest()), | |
| createGcaRequest('chat_console_insights')); | |
| }); | |
| it('translates a request with preamble and options', () => { | |
| const aidaRequest = createAidaDoConversationRequest({ | |
| preamble: 'You are an AI assistant', | |
| options: {temperature: 0.5, model_id: 'test-model'}, | |
| }); | |
| const expectedGcaRequest = createGcaRequest('chat_console_insights', { | |
| model: 'test-model', | |
| systemInstruction: {role: 'user', parts: [{text: 'You are an AI assistant'}]}, | |
| generationConfig: {temperature: 0.5}, | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(aidaRequest), expectedGcaRequest); | |
| }); | |
| it('translates a request with facts', () => { | |
| const aidaRequest = createAidaDoConversationRequest({ | |
| facts: [ | |
| {text: 'Fact 1', metadata: {source: 'src1', score: 1}}, | |
| {text: 'Fact 2', metadata: {source: 'src2', score: 2}}, | |
| ], | |
| }); | |
| const expectedGcaRequest = createGcaRequest('chat_console_insights', { | |
| contents: [ | |
| {role: 'user', parts: [{text: '[source: src1] Fact 1'}, {text: '[source: src2] Fact 2'}]}, | |
| {role: 'user', parts: [{text: 'Hello'}]}, | |
| ], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(aidaRequest), expectedGcaRequest); | |
| }); | |
| it('translates a request with historical contexts', () => { | |
| const aidaRequest = createAidaDoConversationRequest({ | |
| historical_contexts: [ | |
| {parts: [{text: 'Hello'}], role: AidaClient.Role.USER}, | |
| {parts: [{text: 'Hi there!'}], role: AidaClient.Role.MODEL}, | |
| ], | |
| current_message: {parts: [{text: 'How are you?'}], role: AidaClient.Role.USER}, | |
| }); | |
| const expectedGcaRequest = createGcaRequest('chat_console_insights', { | |
| contents: [ | |
| {role: 'user', parts: [{text: 'Hello'}]}, | |
| {role: 'model', parts: [{text: 'Hi there!'}]}, | |
| {role: 'user', parts: [{text: 'How are you?'}]}, | |
| ], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(aidaRequest), expectedGcaRequest); | |
| }); | |
| it('translates a request with function declarations', () => { | |
| const aidaRequest = createAidaDoConversationRequest({ | |
| function_declarations: [{ | |
| name: 'my_func', | |
| description: 'A description', | |
| parameters: { | |
| type: AidaClient.ParametersTypes.OBJECT, | |
| description: 'Params', | |
| properties: {arg1: {type: AidaClient.ParametersTypes.STRING, description: 'An arg'}}, | |
| required: ['arg1'], | |
| }, | |
| }], | |
| }); | |
| const expectedGcaRequest = createGcaRequest('chat_console_insights', { | |
| tools: [{ | |
| functionDeclarations: [{ | |
| name: 'my_func', | |
| description: 'A description', | |
| parameters: { | |
| type: GcaTypes.Type.OBJECT, | |
| description: 'Params', | |
| properties: {arg1: {type: GcaTypes.Type.STRING, description: 'An arg'}}, | |
| required: ['arg1'], | |
| }, | |
| }], | |
| }], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(aidaRequest), expectedGcaRequest); | |
| }); | |
| it('translates a function declaration with array of object parameters', () => { | |
| const aidaRequest = createAidaDoConversationRequest({ | |
| function_declarations: [{ | |
| name: 'set_styles', | |
| description: 'A description', | |
| parameters: { | |
| type: AidaClient.ParametersTypes.OBJECT, | |
| description: 'Params', | |
| properties: { | |
| styles: { | |
| type: AidaClient.ParametersTypes.ARRAY, | |
| description: 'Style list', | |
| items: { | |
| type: AidaClient.ParametersTypes.OBJECT, | |
| description: 'Style item', | |
| properties: { | |
| name: {type: AidaClient.ParametersTypes.STRING, description: 'Property name'}, | |
| value: {type: AidaClient.ParametersTypes.STRING, description: 'Property value'}, | |
| }, | |
| required: ['name', 'value'], | |
| }, | |
| }, | |
| }, | |
| required: ['styles'], | |
| }, | |
| }], | |
| }); | |
| const expectedGcaRequest = createGcaRequest('chat_console_insights', { | |
| tools: [{ | |
| functionDeclarations: [{ | |
| name: 'set_styles', | |
| description: 'A description', | |
| parameters: { | |
| type: GcaTypes.Type.OBJECT, | |
| description: 'Params', | |
| properties: { | |
| styles: { | |
| type: GcaTypes.Type.ARRAY, | |
| description: 'Style list', | |
| items: { | |
| type: GcaTypes.Type.OBJECT, | |
| description: 'Style item', | |
| properties: { | |
| name: {type: GcaTypes.Type.STRING, description: 'Property name'}, | |
| value: {type: GcaTypes.Type.STRING, description: 'Property value'}, | |
| }, | |
| required: ['name', 'value'], | |
| }, | |
| }, | |
| }, | |
| required: ['styles'], | |
| }, | |
| }], | |
| }], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(aidaRequest), expectedGcaRequest); | |
| }); | |
| it('maps client_feature to the corresponding GCA experience', () => { | |
| function translateFeature(clientFeature: AidaClient.ClientFeature): string { | |
| return AidaGcaTranslation | |
| .aidaDoConversationRequestToGcaRequest(createAidaDoConversationRequest({client_feature: clientFeature})) | |
| .aicode.experience; | |
| } | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_CONSOLE_INSIGHTS), 'chat_console_insights'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_STYLING_AGENT), 'chat_freestyler'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_NETWORK_AGENT), 'chat_drjones_network_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_FILE_AGENT), 'chat_drjones_file_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_PATCH_AGENT), 'chat_patch_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_PERFORMANCE_ANNOTATIONS_AGENT), | |
| 'chat_performance_annotations_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_PERFORMANCE_FULL_AGENT), | |
| 'chat_performance_full_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_CONTEXT_SELECTION_AGENT), | |
| 'chat_context_selection_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_ACCESSIBILITY_AGENT), | |
| 'chat_accessibility_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_CONVERSATION_SUMMARY_AGENT), | |
| 'chat_conversation_summary_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_STORAGE_AGENT), 'chat_storage_agent'); | |
| assert.strictEqual(translateFeature(AidaClient.ClientFeature.CHROME_DEVTOOLS_V2_AGENT), 'chat_devtools_v2_agent'); | |
| }); | |
| it('throws an error when client_feature is CLIENT_FEATURE_UNSPECIFIED or unmapped', () => { | |
| const unspecifiedRequest = createAidaDoConversationRequest({ | |
| client_feature: AidaClient.ClientFeature.CLIENT_FEATURE_UNSPECIFIED, | |
| }); | |
| assert.throws(() => AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(unspecifiedRequest), | |
| 'Unsupported client feature for GCA conversation: 0'); | |
| const omittedFeatureRequest = createAidaDoConversationRequest({ | |
| client_feature: undefined, | |
| }); | |
| assert.throws(() => AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(omittedFeatureRequest), | |
| 'Unsupported client feature for GCA conversation: undefined'); | |
| const unknownFeatureRequest = createAidaDoConversationRequest({ | |
| client_feature: 999 as AidaClient.ClientFeature, | |
| }); | |
| assert.throws(() => AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(unknownFeatureRequest), | |
| 'Unsupported client feature for GCA conversation: 999'); | |
| }); | |
| }); | |
| describe('GCA GenerateContentResponse to AIDA DoConversationResponse', () => { | |
| it('translates a basic response', () => { | |
| assert.deepEqual(AidaGcaTranslation.gcaResponseToAidaDoConversationResponse(createGcaResponse()), { | |
| explanation: 'Hello there!', | |
| metadata: {rpcGlobalId: 'response-123'}, | |
| functionCalls: undefined, | |
| completed: true, | |
| }); | |
| }); | |
| it('translates a response with function calls', () => { | |
| const gcaResponse = createGcaResponse({ | |
| candidates: [{ | |
| index: 0, | |
| content: { | |
| role: 'model', | |
| parts: [ | |
| {text: 'I am calling a function.'}, | |
| {functionCall: {name: 'my_func', args: {arg1: 'val1'}}}, | |
| ], | |
| }, | |
| finishReason: GcaTypes.FinishReason.STOP, | |
| safetyRatings: [], | |
| citationMetadata: {citations: []}, | |
| groundingMetadata: {}, | |
| aicodeOutput: {contents: []}, | |
| }], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.gcaResponseToAidaDoConversationResponse(gcaResponse), { | |
| explanation: 'I am calling a function.', | |
| metadata: {rpcGlobalId: 'response-123'}, | |
| functionCalls: [{name: 'my_func', args: {arg1: 'val1'}}], | |
| completed: true, | |
| }); | |
| }); | |
| }); | |
| describe('convert Aida parts to GCA parts', () => { | |
| it('translates function response part', () => { | |
| const aidaRequest = createAidaDoConversationRequest({ | |
| current_message: { | |
| parts: [ | |
| {functionResponse: {name: 'my_func1', response: {result: 'ok'}}}, | |
| {functionResponse: {name: 'my_func2', response: {output: 'abcd'}}}, | |
| {functionResponse: {name: 'my_func3', response: {error: 'error'}}}, | |
| {functionResponse: {name: 'my_func4', response: {somethingElse: 'zxy'}}}, | |
| ], | |
| role: AidaClient.Role.USER, | |
| }, | |
| }); | |
| const gcaRequest = AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(aidaRequest); | |
| assert.deepEqual(gcaRequest.contents[0].parts, [ | |
| {functionResponse: {name: 'my_func1', response: {output: 'ok'}}}, | |
| {functionResponse: {name: 'my_func2', response: {output: 'abcd'}}}, | |
| {functionResponse: {name: 'my_func3', response: {error: 'error'}}}, | |
| {functionResponse: {name: 'my_func4', response: {output: {somethingElse: 'zxy'}}}}, | |
| ]); | |
| }); | |
| it('translates inline data part', () => { | |
| const aidaRequest = createAidaDoConversationRequest({ | |
| current_message: { | |
| parts: [{inlineData: {mimeType: 'image/png', data: 'base64data'}}], | |
| role: AidaClient.Role.USER, | |
| }, | |
| }); | |
| const gcaRequest = AidaGcaTranslation.aidaDoConversationRequestToGcaRequest(aidaRequest); | |
| assert.deepEqual(gcaRequest.contents[0].parts?.[0], { | |
| inlineData: {mimeType: 'image/png', data: 'base64data'}, | |
| }); | |
| }); | |
| }); | |
| describe('AidaClientEvent to GCA TelemetryRequest', () => { | |
| const telemetryCases = [ | |
| { | |
| name: 'translates positive sentiment feedback', | |
| event: {do_conversation_client_event: {user_feedback: {sentiment: AidaClient.Rating.POSITIVE}}}, | |
| expectedMetric: {suggestionInteraction: {interaction: GcaTypes.InteractionType.THUMBS_UP}}, | |
| }, | |
| { | |
| name: 'translates negative sentiment feedback', | |
| event: {do_conversation_client_event: {user_feedback: {sentiment: AidaClient.Rating.NEGATIVE}}}, | |
| expectedMetric: {suggestionInteraction: {interaction: GcaTypes.InteractionType.THUMBS_DOWN}}, | |
| }, | |
| { | |
| name: 'translates code completion impression', | |
| event: { | |
| complete_code_client_event: { | |
| user_impression: { | |
| sample: {sample_id: 1}, | |
| latency: {duration: {seconds: 1, nanos: 500000000}}, | |
| }, | |
| }, | |
| }, | |
| expectedMetric: { | |
| suggestionOffered: { | |
| method: GcaTypes.Method.COMPLETE_CODE, | |
| status: GcaTypes.SuggestionStatus.NO_ERROR, | |
| responseLatency: '1.5s', | |
| }, | |
| }, | |
| }, | |
| { | |
| name: 'translates code completion acceptance', | |
| event: {complete_code_client_event: {user_acceptance: {sample: {sample_id: 5}}}}, | |
| expectedMetric: {suggestionInteraction: {interaction: GcaTypes.InteractionType.ACCEPT, candidateIndex: 5}}, | |
| }, | |
| { | |
| name: 'translates code generation impression', | |
| event: { | |
| generate_code_client_event: { | |
| user_impression: { | |
| sample: {sample_id: 1}, | |
| latency: {duration: {seconds: 2, nanos: 0}}, | |
| }, | |
| }, | |
| }, | |
| expectedMetric: { | |
| suggestionOffered: { | |
| method: GcaTypes.Method.GENERATE_CODE, | |
| status: GcaTypes.SuggestionStatus.NO_ERROR, | |
| responseLatency: '2s', | |
| }, | |
| }, | |
| }, | |
| ]; | |
| for (const {name, event, expectedMetric} of telemetryCases) { | |
| it(name, () => { | |
| const clientEvent = createAidaEvent(event); | |
| const telemetryRequest = AidaGcaTranslation.aidaEventToGcaTelemetryRequest(clientEvent); | |
| assert.lengthOf(telemetryRequest.feedbackMetrics, 1); | |
| assert.strictEqual(telemetryRequest.feedbackMetrics[0].responseId, '123'); | |
| if (expectedMetric.suggestionInteraction) { | |
| assert.deepEqual( | |
| telemetryRequest.feedbackMetrics[0].suggestionInteraction, expectedMetric.suggestionInteraction); | |
| } | |
| if (expectedMetric.suggestionOffered) { | |
| assert.deepEqual(telemetryRequest.feedbackMetrics[0].suggestionOffered, expectedMetric.suggestionOffered); | |
| } | |
| }); | |
| } | |
| it('returns empty metrics when no event is present', () => { | |
| const telemetryRequest = AidaGcaTranslation.aidaEventToGcaTelemetryRequest(createAidaEvent()); | |
| assert.lengthOf(telemetryRequest.feedbackMetrics, 0); | |
| }); | |
| it('handles conversation event without sentiment', () => { | |
| const telemetryRequest = AidaGcaTranslation.aidaEventToGcaTelemetryRequest( | |
| createAidaEvent({do_conversation_client_event: {user_feedback: {}}})); | |
| assert.lengthOf(telemetryRequest.feedbackMetrics, 0); | |
| }); | |
| }); | |
| function createAidaCompletionRequest(overrides: Partial<AidaClient.CompletionRequest> = {}): | |
| AidaClient.CompletionRequest { | |
| const {metadata, ...otherOverrides} = overrides; | |
| return { | |
| client: DEFAULT_CLIENT, | |
| prefix: 'function foo() {', | |
| suffix: '}', | |
| metadata: {...DEFAULT_METADATA, ...metadata}, | |
| ...otherOverrides, | |
| }; | |
| } | |
| describe('AIDA CompletionRequest to GCA GenerateContentRequest', () => { | |
| describeCommonRequestFields(createAidaCompletionRequest, AidaGcaTranslation.aidaCompletionRequestToGcaRequest); | |
| it('translates a basic completion request', () => { | |
| assert.deepEqual(AidaGcaTranslation.aidaCompletionRequestToGcaRequest(createAidaCompletionRequest({ | |
| additional_files: [{ | |
| path: 'fake-path.js', | |
| content: 'description/instructions', | |
| included_reason: AidaClient.Reason.RELATED_FILE, | |
| }], | |
| })), | |
| createGcaRequest('complete_code', { | |
| contents: [], | |
| aicode: { | |
| experience: 'complete_code', | |
| files: [ | |
| { | |
| fileUri: 'devtools-code-completion', | |
| inclusionReason: [GcaTypes.InclusionReason.ACTIVE], | |
| segments: [ | |
| {content: 'function foo() {', isSelected: false}, | |
| {content: '', isSelected: true}, | |
| {content: '}', isSelected: false}, | |
| ], | |
| }, | |
| { | |
| fileUri: 'fake-path.js', | |
| inclusionReason: [GcaTypes.InclusionReason.RELATED], | |
| segments: [{content: 'description/instructions', isSelected: false}], | |
| }, | |
| ], | |
| }, | |
| })); | |
| }); | |
| it('translates a completion request with options and additional files', () => { | |
| const aidaRequest = createAidaCompletionRequest({ | |
| prefix: 'console.log(', | |
| suffix: undefined, | |
| options: { | |
| temperature: 0, | |
| model_id: 'code-model', | |
| inference_language: AidaClient.AidaInferenceLanguage.JAVASCRIPT, | |
| stop_sequences: ['\n'], | |
| }, | |
| metadata: {...DEFAULT_METADATA, string_session_id: 'session-456'}, | |
| last_user_action: AidaClient.EditType.ADD, | |
| additional_files: [ | |
| {path: 'utils.js', content: 'export const log = () => {}', included_reason: AidaClient.Reason.CURRENTLY_OPEN}, | |
| ], | |
| }); | |
| const expectedGcaRequest = createGcaRequest('complete_code', { | |
| contents: [], | |
| model: 'code-model', | |
| generationConfig: {stopSequences: ['\n'], temperature: 0}, | |
| labels: { | |
| inference_language: 'javascript', | |
| last_user_action: 'add', | |
| session_id: 'session-456', | |
| ...DEFAULT_LABELS, | |
| }, | |
| aicode: { | |
| experience: 'complete_code', | |
| files: [ | |
| { | |
| fileUri: 'devtools-code-completion', | |
| inclusionReason: [1], | |
| segments: [{content: 'console.log(', isSelected: false}, {content: '', isSelected: true}], | |
| }, | |
| { | |
| fileUri: 'utils.js', | |
| inclusionReason: [GcaTypes.InclusionReason.OPEN], | |
| segments: [{content: 'export const log = () => {}', isSelected: false}], | |
| }, | |
| ], | |
| }, | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.aidaCompletionRequestToGcaRequest(aidaRequest), expectedGcaRequest); | |
| }); | |
| }); | |
| describe('GCA GenerateContentResponse to AIDA CompletionResponse', () => { | |
| it('translates a basic completion response', () => { | |
| const gcaResponse = createGcaResponse({ | |
| modelVersion: 'code-model', | |
| responseId: 'response-456', | |
| candidates: [{ | |
| index: 0, | |
| content: {role: 'model', parts: [{text: '"hello")'}]}, | |
| finishReason: GcaTypes.FinishReason.STOP, | |
| safetyRatings: [], | |
| citationMetadata: {citations: []}, | |
| groundingMetadata: {}, | |
| aicodeOutput: {contents: []}, | |
| }], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.gcaResponseToAidaCompletionResponse(gcaResponse), { | |
| generatedSamples: [{ | |
| generationString: '"hello")', | |
| score: 0, | |
| sampleId: 0, | |
| attributionMetadata: {attributionAction: AidaClient.RecitationAction.CITE, citations: []}, | |
| }], | |
| metadata: {rpcGlobalId: 'response-456'}, | |
| }); | |
| }); | |
| }); | |
| function createAidaGenerateCodeRequest(overrides: Partial<AidaClient.GenerateCodeRequest> = {}): | |
| AidaClient.GenerateCodeRequest { | |
| const {metadata, ...otherOverrides} = overrides; | |
| return { | |
| client: DEFAULT_CLIENT, | |
| preamble: 'Generate a function', | |
| current_message: {parts: [{text: 'that adds two numbers'}], role: AidaClient.Role.USER}, | |
| use_case: AidaClient.UseCase.CODE_GENERATION, | |
| metadata: {...DEFAULT_METADATA, ...metadata}, | |
| ...otherOverrides, | |
| }; | |
| } | |
| describe('AIDA GenerateCodeRequest to GCA GenerateContentRequest', () => { | |
| describeCommonRequestFields(createAidaGenerateCodeRequest, AidaGcaTranslation.aidaGenerateCodeRequestToGcaRequest); | |
| it('translates a basic generate code request', () => { | |
| assert.deepEqual(AidaGcaTranslation.aidaGenerateCodeRequestToGcaRequest(createAidaGenerateCodeRequest()), { | |
| aicode: {experience: 'generate_code'}, | |
| contents: [{role: 'user', parts: [{text: 'that adds two numbers'}]}], | |
| systemInstruction: {role: 'user', parts: [{text: 'Generate a function'}]}, | |
| labels: {use_case: 'code_generation', ...DEFAULT_LABELS}, | |
| }); | |
| }); | |
| it('translates a generate code request with options and context files', () => { | |
| const aidaRequest = createAidaGenerateCodeRequest({ | |
| preamble: 'Help me with this', | |
| current_message: {parts: [{text: 'fix the bug'}], role: AidaClient.Role.USER}, | |
| options: { | |
| temperature: 0.7, | |
| model_id: 'gen-model', | |
| inference_language: AidaClient.AidaInferenceLanguage.TYPESCRIPT, | |
| expect_code_output: true, | |
| }, | |
| client_feature: AidaClient.ClientFeature.CHROME_FILE_AGENT, | |
| metadata: {...DEFAULT_METADATA, string_session_id: 'session-789'}, | |
| context_files: [{ | |
| path: 'app.ts', | |
| full_content: 'console.log("bug")', | |
| programming_language: AidaClient.AidaInferenceLanguage.TYPESCRIPT, | |
| }], | |
| }); | |
| const expectedGcaRequest = createGcaRequest('generate_code', { | |
| contents: [{role: 'user', parts: [{text: 'fix the bug'}]}], | |
| systemInstruction: {role: 'user', parts: [{text: 'Help me with this'}]}, | |
| model: 'gen-model', | |
| generationConfig: {temperature: 0.7}, | |
| labels: { | |
| inference_language: 'typescript', | |
| expect_code_output: 'true', | |
| client_feature: 'chrome_file_agent', | |
| session_id: 'session-789', | |
| use_case: 'code_generation', | |
| ...DEFAULT_LABELS, | |
| }, | |
| aicode: { | |
| experience: 'generate_code', | |
| files: [{fileUri: 'app.ts', programmingLanguage: 'TYPESCRIPT'}], | |
| }, | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.aidaGenerateCodeRequestToGcaRequest(aidaRequest), expectedGcaRequest); | |
| }); | |
| }); | |
| describe('GCA GenerateContentResponse to AIDA GenerateCodeResponse', () => { | |
| it('translates a basic generate code response', () => { | |
| const gcaResponse = createGcaResponse({ | |
| modelVersion: 'gen-model', | |
| responseId: 'response-789', | |
| candidates: [{ | |
| index: 0, | |
| content: {role: 'model', parts: [{text: 'const add = (a, b) => a + b;'}]}, | |
| finishReason: GcaTypes.FinishReason.STOP, | |
| safetyRatings: [], | |
| citationMetadata: {citations: []}, | |
| groundingMetadata: {}, | |
| aicodeOutput: {contents: []}, | |
| }], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.gcaResponseToAidaGenerateCodeResponse(gcaResponse), { | |
| samples: [{ | |
| generationString: 'const add = (a, b) => a + b;', | |
| score: 0, | |
| sampleId: 0, | |
| attributionMetadata: {attributionAction: AidaClient.RecitationAction.CITE, citations: []}, | |
| }], | |
| metadata: {rpcGlobalId: 'response-789'}, | |
| }); | |
| }); | |
| }); | |
| describe('GCA GenerateContentResponse to AIDA ChunkResponse', () => { | |
| it('translates a basic chunk response and includes modelVersion', () => { | |
| const gcaResponse = createGcaResponse({ | |
| modelVersion: 'gen-model', | |
| responseId: 'response-789', | |
| candidates: [{ | |
| index: 0, | |
| content: {role: 'model', parts: [{text: 'const add = (a, b) => a + b;'}]}, | |
| finishReason: GcaTypes.FinishReason.STOP, | |
| safetyRatings: [], | |
| citationMetadata: {citations: []}, | |
| groundingMetadata: {}, | |
| aicodeOutput: {contents: []}, | |
| }], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.gcaChunkResponseToAidaChunkResponse(gcaResponse), [{ | |
| textChunk: { | |
| text: 'const add = (a, b) => a + b;', | |
| }, | |
| metadata: { | |
| rpcGlobalId: 'response-789', | |
| attributionMetadata: { | |
| attributionAction: AidaClient.RecitationAction.CITE, | |
| citations: [], | |
| }, | |
| inferenceOptionMetadata: {modelId: 'gen-model'}, | |
| }, | |
| }]); | |
| }); | |
| it('translates a chunk response with an error without throwing', () => { | |
| const gcaResponse = createGcaResponse({ | |
| error: {code: 429, message: 'Quota exceeded', status: 'RESOURCE_EXHAUSTED'}, | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.gcaChunkResponseToAidaChunkResponse(gcaResponse), [{ | |
| error: {code: 429, message: 'Quota exceeded', status: 'RESOURCE_EXHAUSTED'}, | |
| }]); | |
| }); | |
| }); | |
| it('translates a functionCallChunk with thoughtSignature', () => { | |
| const gcaResponse = createGcaResponse({ | |
| modelVersion: 'gen-model', | |
| responseId: 'response-789', | |
| candidates: [{ | |
| index: 0, | |
| content: { | |
| role: 'model', | |
| parts: [{functionCall: {name: 'getStyles', args: {uid: 1}}, thoughtSignature: 'thought-sig-abc'}], | |
| }, | |
| finishReason: GcaTypes.FinishReason.STOP, | |
| safetyRatings: [], | |
| citationMetadata: {citations: []}, | |
| groundingMetadata: {}, | |
| aicodeOutput: {contents: []}, | |
| }], | |
| }); | |
| assert.deepEqual(AidaGcaTranslation.gcaChunkResponseToAidaChunkResponse(gcaResponse), [ | |
| { | |
| functionCallChunk: { | |
| functionCall: { | |
| name: 'getStyles', | |
| args: {uid: 1}, | |
| thoughtSignature: 'thought-sig-abc', | |
| }, | |
| }, | |
| metadata: { | |
| rpcGlobalId: 'response-789', | |
| inferenceOptionMetadata: {modelId: 'gen-model'}, | |
| attributionMetadata: {attributionAction: AidaClient.RecitationAction.CITE, citations: []}, | |
| }, | |
| }, | |
| ]); | |
| }); | |
| }); |