Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/extension/extension/vscode-node/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import { IWorkspaceListenerService } from '../../workspaceRecorder/common/worksp
import { WorkspacListenerService } from '../../workspaceRecorder/vscode-node/workspaceListenerService';
import { registerServices as registerCommonServices } from '../vscode/services';
import { NativeEnvServiceImpl } from '../../../platform/env/vscode-node/nativeEnvServiceImpl';
import { GithubAvailableEmbeddingTypesService, IGithubAvailableEmbeddingTypesService } from '../../../platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes';

// ###########################################################################################
// ### ###
Expand Down Expand Up @@ -195,6 +196,7 @@ export function registerServices(builder: IInstantiationServiceBuilder, extensio
builder.define(IWorkspaceListenerService, new SyncDescriptor(WorkspacListenerService));
builder.define(ICodeSearchAuthenticationService, new SyncDescriptor(VsCodeCodeSearchAuthenticationService));
builder.define(ITodoListContextProvider, new SyncDescriptor(TodoListContextProvider));
builder.define(IGithubAvailableEmbeddingTypesService, new SyncDescriptor(GithubAvailableEmbeddingTypesService));
}

function setupMSFTExperimentationService(builder: IInstantiationServiceBuilder, extensionContext: ExtensionContext) {
Expand Down
2 changes: 2 additions & 0 deletions src/extension/test/node/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { SimulationAlternativeNotebookContentService, SimulationNotebookService,
import { NullTestProvider } from '../../../platform/testing/common/nullTestProvider';
import { TestLogService } from '../../../platform/testing/common/testLogService';
import { ITestProvider } from '../../../platform/testing/common/testProvider';
import { IGithubAvailableEmbeddingTypesService, MockGithubAvailableEmbeddingTypesService } from '../../../platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes';
import { IWorkspaceChunkSearchService, NullWorkspaceChunkSearchService } from '../../../platform/workspaceChunkSearch/node/workspaceChunkSearchService';
import { DisposableStore } from '../../../util/vs/base/common/lifecycle';
import { SyncDescriptor } from '../../../util/vs/platform/instantiation/common/descriptors';
Expand Down Expand Up @@ -102,5 +103,6 @@ export function createExtensionUnitTestingServices(disposables: Pick<DisposableS
testingServiceCollection.define(ITodoListContextProvider, new SyncDescriptor(TodoListContextProvider));
testingServiceCollection.define(ILanguageModelServer, new SyncDescriptor(MockLanguageModelServer));
testingServiceCollection.define(IEditToolLearningService, new SyncDescriptor(EditToolLearningService));
testingServiceCollection.define(IGithubAvailableEmbeddingTypesService, new SyncDescriptor(MockGithubAvailableEmbeddingTypesService));
return testingServiceCollection;
}
2 changes: 2 additions & 0 deletions src/extension/test/vscode-node/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import { TestProvider } from '../../../platform/testing/vscode/testProviderImpl'
import { ITokenizerProvider, TokenizerProvider } from '../../../platform/tokenizer/node/tokenizer';
import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
import { ExtensionTextDocumentManager } from '../../../platform/workspace/vscode/workspaceServiceImpl';
import { GithubAvailableEmbeddingTypesService, IGithubAvailableEmbeddingTypesService } from '../../../platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes';
import { SyncDescriptor } from '../../../util/vs/platform/instantiation/common/descriptors';
import { CommandServiceImpl, ICommandService } from '../../commands/node/commandService';
import { IPromptWorkspaceLabels, PromptWorkspaceLabels } from '../../context/node/resolvers/promptWorkspaceLabels';
Expand Down Expand Up @@ -180,6 +181,7 @@ export function createExtensionTestingServices(): TestingServiceCollection {
testingServiceCollection.define(IToolGroupingCache, new SyncDescriptor(ToolGroupingCache));
testingServiceCollection.define(IToolGroupingService, new SyncDescriptor(ToolGroupingService));
testingServiceCollection.define(ITodoListContextProvider, new SyncDescriptor(TodoListContextProvider));
testingServiceCollection.define(IGithubAvailableEmbeddingTypesService, new SyncDescriptor(GithubAvailableEmbeddingTypesService));

return testingServiceCollection;
}
Expand Down
4 changes: 2 additions & 2 deletions src/extension/tools/node/githubRepoTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { GithubRepoId, toGithubNwo } from '../../../platform/git/common/gitServi
import { IGithubCodeSearchService } from '../../../platform/remoteCodeSearch/common/githubCodeSearchService';
import { RemoteCodeSearchIndexStatus } from '../../../platform/remoteCodeSearch/common/remoteCodeSearch';
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';
import { GithubAvailableEmbeddingTypesManager } from '../../../platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes';
import { GithubAvailableEmbeddingTypesService, IGithubAvailableEmbeddingTypesService } from '../../../platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes';
import { Result } from '../../../util/common/result';
import { TelemetryCorrelationId } from '../../../util/common/telemetryCorrelationId';
import { isLocation, isUri } from '../../../util/common/types';
Expand Down Expand Up @@ -42,12 +42,12 @@ interface PrepareError {
export class GithubRepoTool implements ICopilotTool<GithubRepoToolParams> {
public static readonly toolName = ToolName.GithubRepo;

private readonly _availableEmbeddingTypesManager = new Lazy<GithubAvailableEmbeddingTypesManager>(() => this._instantiationService.createInstance(GithubAvailableEmbeddingTypesManager));

constructor(
@IRunCommandExecutionService _commandService: IRunCommandExecutionService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IGithubCodeSearchService private readonly _githubCodeSearch: IGithubCodeSearchService,
@IGithubAvailableEmbeddingTypesService private readonly _availableEmbeddingTypesManager: Lazy<GithubAvailableEmbeddingTypesService>,
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter type annotation is incorrect. It should be IGithubAvailableEmbeddingTypesService not Lazy<GithubAvailableEmbeddingTypesService> since the service is now injected directly through DI rather than being lazy-loaded.

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjbvz yeah, actually this is interesting. Intentional?

@ITelemetryService private readonly _telemetryService: ITelemetryService,
) { }

Expand Down
4 changes: 3 additions & 1 deletion src/platform/embeddings/common/embeddingsComputer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,10 @@ export interface EmbeddingDistance {

export const IEmbeddingsComputer = createServiceIdentifier<IEmbeddingsComputer>('IEmbeddingsComputer');

export type EmbeddingInputType = 'document' | 'query';

export type ComputeEmbeddingsOptions = {
readonly inputType?: 'document' | 'query';
readonly inputType?: EmbeddingInputType;
};

export interface IEmbeddingsComputer {
Expand Down
25 changes: 16 additions & 9 deletions src/platform/urlChunkSearch/node/urlChunkEmbeddingsIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import { URI } from '../../../util/vs/base/common/uri';
import { IAuthenticationService } from '../../authentication/common/authentication';
import { FileChunkAndScore, FileChunkWithEmbedding } from '../../chunking/common/chunk';
import { ChunkableContent, ComputeBatchInfo, EmbeddingsComputeQos, IChunkingEndpointClient } from '../../chunking/common/chunkingEndpointClient';
import { distance, Embedding, EmbeddingType, IEmbeddingsComputer } from '../../embeddings/common/embeddingsComputer';
import { distance, Embedding, EmbeddingInputType, EmbeddingType, IEmbeddingsComputer } from '../../embeddings/common/embeddingsComputer';
import { ILogService } from '../../log/common/logService';
import { IGithubAvailableEmbeddingTypesService } from '../../workspaceChunkSearch/common/githubAvailableEmbeddingTypes';

/**
* The maximum content length to sent to the chunking endpoint.
Expand Down Expand Up @@ -51,6 +52,7 @@ export class UrlChunkEmbeddingsIndex extends Disposable {
@ILogService private readonly _logService: ILogService,
@IEmbeddingsComputer private readonly _embeddingsComputer: IEmbeddingsComputer,
@IChunkingEndpointClient private readonly _chunkingEndpointClient: IChunkingEndpointClient,
@IGithubAvailableEmbeddingTypesService private readonly _availableEmbeddingTypesService: IGithubAvailableEmbeddingTypesService,
) {
super();
}
Expand All @@ -60,20 +62,25 @@ export class UrlChunkEmbeddingsIndex extends Disposable {
query: string,
token: CancellationToken,
): Promise<FileChunkAndScore[][]> {
const embeddingType = await raceCancellationError(this._availableEmbeddingTypesService.getPreferredType(/*silent*/ false), token);
if (!embeddingType) {
throw new Error('No embedding types available');
}

const [queryEmbedding, fileChunksAndEmbeddings] = await raceCancellationError(Promise.all([
this.computeEmbeddings(query, token),
this.getEmbeddingsForFiles(files.map(file => new UrlContent(file.uri, file.content)), EmbeddingsComputeQos.Batch, token)
this.computeEmbeddings(embeddingType, query, 'query', token),
this.getEmbeddingsForFiles(embeddingType, files.map(file => new UrlContent(file.uri, file.content)), EmbeddingsComputeQos.Batch, token)
]), token);

return this.computeChunkScores(fileChunksAndEmbeddings, queryEmbedding);
}

private async computeEmbeddings(str: string, token: CancellationToken): Promise<Embedding> {
const embeddings = await this._embeddingsComputer.computeEmbeddings(EmbeddingType.text3small_512, [str], {}, new TelemetryCorrelationId('UrlChunkEmbeddingsIndex::computeEmbeddings'), token);
private async computeEmbeddings(embeddingType: EmbeddingType, str: string, inputType: EmbeddingInputType, token: CancellationToken): Promise<Embedding> {
const embeddings = await this._embeddingsComputer.computeEmbeddings(embeddingType, [str], { inputType }, new TelemetryCorrelationId('UrlChunkEmbeddingsIndex::computeEmbeddings'), token);
return embeddings.values[0];
}

private async getEmbeddingsForFiles(files: readonly UrlContent[], qos: EmbeddingsComputeQos, token: CancellationToken): Promise<(readonly FileChunkWithEmbedding[])[]> {
private async getEmbeddingsForFiles(embeddingType: EmbeddingType, files: readonly UrlContent[], qos: EmbeddingsComputeQos, token: CancellationToken): Promise<(readonly FileChunkWithEmbedding[])[]> {
if (!files.length) {
return [];
}
Expand All @@ -88,7 +95,7 @@ export class UrlChunkEmbeddingsIndex extends Disposable {
}

const result = await Promise.all(files.map(async file => {
const result = await this.getChunksAndEmbeddings(authToken, file, batchInfo, qos, token);
const result = await this.getChunksAndEmbeddings(authToken, embeddingType, file, batchInfo, qos, token);
if (!result) {
return [];
}
Expand All @@ -107,13 +114,13 @@ export class UrlChunkEmbeddingsIndex extends Disposable {
);
}

private async getChunksAndEmbeddings(authToken: string, content: UrlContent, batchInfo: ComputeBatchInfo, qos: EmbeddingsComputeQos, token: CancellationToken): Promise<readonly FileChunkWithEmbedding[] | undefined> {
private async getChunksAndEmbeddings(authToken: string, embeddingType: EmbeddingType, content: UrlContent, batchInfo: ComputeBatchInfo, qos: EmbeddingsComputeQos, token: CancellationToken): Promise<readonly FileChunkWithEmbedding[] | undefined> {
const existing = await raceCancellationError(this._cache.get(content), token);
if (existing) {
return existing;
}

const chunksAndEmbeddings = await raceCancellationError(this._chunkingEndpointClient.computeChunksAndEmbeddings(authToken, EmbeddingType.text3small_512, content, batchInfo, qos, new Map(), new CallTracker('UrlChunkEmbeddingsIndex::getChunksAndEmbeddings'), token), token);
const chunksAndEmbeddings = await raceCancellationError(this._chunkingEndpointClient.computeChunksAndEmbeddings(authToken, embeddingType, content, batchInfo, qos, new Map(), new CallTracker('UrlChunkEmbeddingsIndex::getChunksAndEmbeddings'), token), token);
if (chunksAndEmbeddings) {
this._cache.set(content, chunksAndEmbeddings);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { RequestType } from '@vscode/copilot-api';
import { createRequestHMAC } from '../../../util/common/crypto';
import { Result } from '../../../util/common/result';
import { createServiceIdentifier } from '../../../util/common/services';
import { CallTracker } from '../../../util/common/telemetryCorrelationId';
import { env } from '../../../util/vs/base/common/process';
import { generateUuid } from '../../../util/vs/base/common/uuid';
Expand Down Expand Up @@ -35,7 +36,22 @@ type GetAvailableTypesError =

type GetAvailableTypesResult = Result<AvailableEmbeddingTypes, GetAvailableTypesError>;

export class GithubAvailableEmbeddingTypesManager {
export const IGithubAvailableEmbeddingTypesService = createServiceIdentifier<IGithubAvailableEmbeddingTypesService>('IGithubAvailableEmbeddingTypesService');

export interface IGithubAvailableEmbeddingTypesService {
readonly _serviceBrand: undefined;

/**
* Gets the preferred embedding type based on available types and user configuration.
* @param silent Whether to silently handle authentication errors
* @returns The preferred embedding type or undefined if none available
*/
getPreferredType(silent: boolean): Promise<EmbeddingType | undefined>;
}

export class GithubAvailableEmbeddingTypesService implements IGithubAvailableEmbeddingTypesService {

readonly _serviceBrand: undefined;

private _cached?: Promise<GetAvailableTypesResult>;

Expand Down Expand Up @@ -213,3 +229,12 @@ export class GithubAvailableEmbeddingTypesManager {
return all.primary.at(0) ?? all.deprecated.at(0);
}
}


export class MockGithubAvailableEmbeddingTypesService implements IGithubAvailableEmbeddingTypesService {
declare readonly _serviceBrand: undefined;

async getPreferredType(_silent: boolean): Promise<EmbeddingType | undefined> {
return EmbeddingType.metis_1024_I16_Binary;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { ISimulationTestContext } from '../../simulationTestContext/common/simul
import { IExperimentationService } from '../../telemetry/common/nullExperimentationService';
import { ITelemetryService } from '../../telemetry/common/telemetry';
import { getWorkspaceFileDisplayPath, IWorkspaceService } from '../../workspace/common/workspaceService';
import { GithubAvailableEmbeddingTypesManager } from '../common/githubAvailableEmbeddingTypes';
import { IGithubAvailableEmbeddingTypesService } from '../common/githubAvailableEmbeddingTypes';
import { IWorkspaceChunkSearchStrategy, StrategySearchResult, StrategySearchSizing, WorkspaceChunkQuery, WorkspaceChunkQueryWithEmbeddings, WorkspaceChunkSearchOptions, WorkspaceChunkSearchStrategyId, WorkspaceSearchAlert } from '../common/workspaceChunkSearch';
import { CodeSearchChunkSearch, CodeSearchRemoteIndexState } from './codeSearchChunkSearch';
import { EmbeddingsChunkSearch, LocalEmbeddingsIndexState, LocalEmbeddingsIndexStatus } from './embeddingsChunkSearch';
Expand Down Expand Up @@ -115,17 +115,15 @@ export class WorkspaceChunkSearchService extends Disposable implements IWorkspac
readonly onDidChangeIndexState = this._onDidChangeIndexState.event;

private _impl: WorkspaceChunkSearchServiceImpl | undefined;
private readonly _availableEmbeddingTypes: GithubAvailableEmbeddingTypesManager;

constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IAuthenticationService private readonly _authenticationService: IAuthenticationService,
@IGithubAvailableEmbeddingTypesService private readonly _availableEmbeddingTypes: IGithubAvailableEmbeddingTypesService,
@ILogService private readonly _logService: ILogService,
) {
super();

this._availableEmbeddingTypes = _instantiationService.createInstance(GithubAvailableEmbeddingTypesManager);

this.tryInit(true);
}

Expand Down
2 changes: 2 additions & 0 deletions test/base/simulationContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { SimulationReviewService } from '../../src/platform/test/node/simulation
import { NullTestProvider } from '../../src/platform/testing/common/nullTestProvider';
import { ITestProvider } from '../../src/platform/testing/common/testProvider';
import { ITokenizerProvider, TokenizerProvider } from '../../src/platform/tokenizer/node/tokenizer';
import { GithubAvailableEmbeddingTypesService, IGithubAvailableEmbeddingTypesService } from '../../src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes';
import { IWorkspaceChunkSearchService, WorkspaceChunkSearchService } from '../../src/platform/workspaceChunkSearch/node/workspaceChunkSearchService';
import { IWorkspaceFileIndex, WorkspaceFileIndex } from '../../src/platform/workspaceChunkSearch/node/workspaceFileIndex';
import { createServiceIdentifier } from '../../src/util/common/services';
Expand Down Expand Up @@ -292,6 +293,7 @@ export async function createSimulationAccessor(
testingServiceCollection.define(IGitExtensionService, new SyncDescriptor(NullGitExtensionService));
testingServiceCollection.define(IReleaseNotesService, new SyncDescriptor(ReleaseNotesService));
testingServiceCollection.define(IWorkspaceFileIndex, new SyncDescriptor(WorkspaceFileIndex));
testingServiceCollection.define(IGithubAvailableEmbeddingTypesService, new SyncDescriptor(GithubAvailableEmbeddingTypesService));

if (opts.useExperimentalCodeSearchService) {
testingServiceCollection.define(IWorkspaceChunkSearchService, new SyncDescriptor(SimulationCodeSearchChunkSearchService, []));
Expand Down