|
| 1 | +// Copyright 2025 The Chromium Authors |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +import * as Host from '../../core/host/host.js'; |
| 6 | +import * as Root from '../../core/root/root.js'; |
| 7 | + |
| 8 | +import {debugLog} from './debug.js'; |
| 9 | + |
| 10 | +export const basePreamble = |
| 11 | + `You are a highly skilled senior software engineer with deep expertise across multiple web technologies and programming languages, including JavaScript, TypeScript, HTML, and CSS. |
| 12 | +Your role is to act as an expert pair programmer within the Chrome DevTools environment. |
| 13 | +
|
| 14 | +**Core Directives (Adhere to these strictly):** |
| 15 | +
|
| 16 | +1. **Language and Quality:** |
| 17 | + * Generate code that is modern, efficient, and idiomatic for the inferred language (e.g., modern JavaScript/ES6+, semantic HTML5, efficient CSS). |
| 18 | + * Where appropriate, include basic error handling (e.g., for API calls). |
| 19 | +`; |
| 20 | + |
| 21 | +export const additionalContextForConsole = ` |
| 22 | +You are operating within the execution environment of the Chrome DevTools Console. |
| 23 | +The console has direct access to the inspected page's \`window\` and \`document\`. |
| 24 | +
|
| 25 | +* **Utilize Console Utilities:** You have access to the Console Utilities API. You **should** use these helper functions and variables when they are the most direct way to accomplish the user's goal. |
| 26 | +`; |
| 27 | + |
| 28 | +interface Options { |
| 29 | + aidaClient: Host.AidaClient.AidaClient; |
| 30 | + serverSideLoggingEnabled?: boolean; |
| 31 | + confirmSideEffectForTest?: typeof Promise.withResolvers; |
| 32 | +} |
| 33 | + |
| 34 | +interface RequestOptions { |
| 35 | + temperature?: number; |
| 36 | + modelId?: string; |
| 37 | +} |
| 38 | + |
| 39 | +/** |
| 40 | + * The AiCodeGeneration class is responsible for fetching generated code suggestions |
| 41 | + * from the AIDA backend. |
| 42 | + */ |
| 43 | +export class AiCodeGeneration { |
| 44 | + readonly #sessionId: string = crypto.randomUUID(); |
| 45 | + readonly #aidaClient: Host.AidaClient.AidaClient; |
| 46 | + readonly #serverSideLoggingEnabled: boolean; |
| 47 | + |
| 48 | + constructor(opts: Options) { |
| 49 | + this.#aidaClient = opts.aidaClient; |
| 50 | + this.#serverSideLoggingEnabled = opts.serverSideLoggingEnabled ?? false; |
| 51 | + } |
| 52 | + |
| 53 | + #buildRequest( |
| 54 | + prompt: string, |
| 55 | + preamble: string, |
| 56 | + inferenceLanguage: Host.AidaClient.AidaInferenceLanguage = Host.AidaClient.AidaInferenceLanguage.JAVASCRIPT, |
| 57 | + ): Host.AidaClient.GenerateCodeRequest { |
| 58 | + const userTier = Host.AidaClient.convertToUserTierEnum(this.#userTier); |
| 59 | + function validTemperature(temperature: number|undefined): number|undefined { |
| 60 | + return typeof temperature === 'number' && temperature >= 0 ? temperature : undefined; |
| 61 | + } |
| 62 | + return { |
| 63 | + client: Host.AidaClient.CLIENT_NAME, |
| 64 | + preamble, |
| 65 | + current_message: { |
| 66 | + parts: [{ |
| 67 | + text: prompt, |
| 68 | + }], |
| 69 | + role: Host.AidaClient.Role.USER, |
| 70 | + }, |
| 71 | + use_case: Host.AidaClient.UseCase.CODE_GENERATION, |
| 72 | + options: { |
| 73 | + inference_language: inferenceLanguage, |
| 74 | + temperature: validTemperature(this.#options.temperature), |
| 75 | + model_id: this.#options.modelId || undefined, |
| 76 | + }, |
| 77 | + metadata: { |
| 78 | + disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false), |
| 79 | + string_session_id: this.#sessionId, |
| 80 | + user_tier: userTier, |
| 81 | + client_version: Root.Runtime.getChromeVersion(), |
| 82 | + }, |
| 83 | + }; |
| 84 | + } |
| 85 | + |
| 86 | + get #userTier(): string|undefined { |
| 87 | + return Root.Runtime.hostConfig.devToolsAiCodeGeneration?.userTier; |
| 88 | + } |
| 89 | + |
| 90 | + get #options(): RequestOptions { |
| 91 | + const temperature = Root.Runtime.hostConfig.devToolsAiCodeGeneration?.temperature; |
| 92 | + const modelId = Root.Runtime.hostConfig.devToolsAiCodeGeneration?.modelId; |
| 93 | + |
| 94 | + return { |
| 95 | + temperature, |
| 96 | + modelId, |
| 97 | + }; |
| 98 | + } |
| 99 | + |
| 100 | + registerUserImpression(rpcGlobalId: Host.AidaClient.RpcGlobalId, latency: number, sampleId?: number): void { |
| 101 | + const seconds = Math.floor(latency / 1_000); |
| 102 | + const remainingMs = latency % 1_000; |
| 103 | + const nanos = Math.floor(remainingMs * 1_000_000); |
| 104 | + |
| 105 | + void this.#aidaClient.registerClientEvent({ |
| 106 | + corresponding_aida_rpc_global_id: rpcGlobalId, |
| 107 | + disable_user_content_logging: true, |
| 108 | + generate_code_client_event: { |
| 109 | + user_impression: { |
| 110 | + sample: { |
| 111 | + sample_id: sampleId, |
| 112 | + }, |
| 113 | + latency: { |
| 114 | + duration: { |
| 115 | + seconds, |
| 116 | + nanos, |
| 117 | + }, |
| 118 | + } |
| 119 | + }, |
| 120 | + }, |
| 121 | + }); |
| 122 | + debugLog('Registered user impression with latency {seconds:', seconds, ', nanos:', nanos, '}'); |
| 123 | + Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeGenerationSuggestionDisplayed); |
| 124 | + } |
| 125 | + |
| 126 | + registerUserAcceptance(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId?: number): void { |
| 127 | + void this.#aidaClient.registerClientEvent({ |
| 128 | + corresponding_aida_rpc_global_id: rpcGlobalId, |
| 129 | + disable_user_content_logging: true, |
| 130 | + generate_code_client_event: { |
| 131 | + user_acceptance: { |
| 132 | + sample: { |
| 133 | + sample_id: sampleId, |
| 134 | + } |
| 135 | + }, |
| 136 | + }, |
| 137 | + }); |
| 138 | + debugLog('Registered user acceptance'); |
| 139 | + Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeGenerationSuggestionAccepted); |
| 140 | + } |
| 141 | + |
| 142 | + async generateCode(prompt: string, preamble: string, inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage): |
| 143 | + Promise<Host.AidaClient.GenerateCodeResponse|null> { |
| 144 | + const request = this.#buildRequest(prompt, preamble, inferenceLanguage); |
| 145 | + const response = await this.#aidaClient.generateCode(request); |
| 146 | + |
| 147 | + debugLog({request, response}); |
| 148 | + |
| 149 | + return response; |
| 150 | + } |
| 151 | +} |
0 commit comments