Path: blob/main/extensions/copilot/test/base/completionsCache.ts
13388 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import { FetchResponse } from '../../src/platform/nesFetch/node/completionsFetchServiceImpl';5import { getRequestId } from '../../src/platform/networking/common/fetch';6import { IHeaders, ReportFetchEvent, Response } from '../../src/platform/networking/common/fetcherService';7import { AsyncIterableObject } from '../../src/util/vs/base/common/async';8import { SQLiteSlottedCache } from './cache';9import { CachedResponseMetadata } from './cachingChatMLFetcher';10import { CacheableCompletionRequest } from './cachingCompletionsFetchService';11import { CurrentTestRunInfo } from './simulationContext';1213export interface ICacheableCompletionsResponse {14readonly requestId: string;15readonly cacheMetadata: CachedResponseMetadata;16readonly status: number;17readonly statusText: string;18readonly body: string;19}2021export namespace ICacheableCompletionsResponse {2223export function create(requestId: string, cacheMetadata: CachedResponseMetadata, status: number, statusText: string, body: string): ICacheableCompletionsResponse {24return { requestId, cacheMetadata, status, statusText, body };25}2627export function isICacheableResponse(obj: unknown): obj is ICacheableCompletionsResponse {28return (29typeof obj === 'object' &&30obj !== null &&31'requestId' in obj &&32typeof (obj as any).requestId === 'string' &&33'cacheMetadata' in obj &&34CachedResponseMetadata.isCachedResponseMetadata((obj as any).cacheMetadata) &&35'status' in obj &&36typeof (obj as any).status === 'number' &&37'statusText' in obj &&38typeof (obj as any).statusText === 'string' &&39'body' in obj &&40typeof (obj as any).body === 'string'41);42}4344export function toFetchResponse(v: ICacheableCompletionsResponse): FetchResponse {45// @ulugbekna: currently, if we don't chunk up, the streaming logic errors out if the stream eventually errored (eg "response too long"),46// but we want to be able to capture edits proposed before the error47const bodyStream = stringToChunkedStream(v.body, 512 /* arbitrary chunk size to hit fast/correct balance */);4849const headers = new Headers(); // @ulugbekna: we don't use headers, so this should be ok for now5051const response = emptyFetcherResponse(headers);5253return {54status: v.status,55statusText: v.statusText,56body: bodyStream,57headers,58requestId: getRequestId(headers),59response,60};61}6263function stringToChunkedStream(str: string, chunkSize: number) {64return new AsyncIterableObject<string>(emitter => {65for (let i = 0; i < str.length; i += chunkSize) {66emitter.emitOne(str.slice(i, i + chunkSize));67}68});69}70}7172export interface ICompletionsCache {73get(req: CacheableCompletionRequest, cacheSlot: number): Promise<ICacheableCompletionsResponse | undefined>;74set(req: CacheableCompletionRequest, cacheSlot: number, cachedResponse: ICacheableCompletionsResponse): Promise<void>;75}7677export class CompletionsSQLiteCache extends SQLiteSlottedCache<CacheableCompletionRequest, ICacheableCompletionsResponse> implements ICompletionsCache {78constructor(salt: string, info: CurrentTestRunInfo) {79super('completions', salt, info);80}81}8283export function emptyFetcherResponse(headers: IHeaders, reportEvent: ReportFetchEvent = () => { }): Response {84return new Response(85200,86'',87headers,88null,89'electron-fetch',90reportEvent,91'test',92'test',93);94}959697