Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vscode] Stub Chat and Language Model API #13778

Merged
merged 2 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
49 changes: 46 additions & 3 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import type * as theia from '@theia/plugin';
import { CommandRegistryImpl } from './command-registry';
import { Emitter } from '@theia/core/lib/common/event';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { CancellationError, CancellationToken, CancellationTokenSource } from '@theia/core/lib/common/cancellation';
import { QuickOpenExtImpl } from './quick-open';
import {
Expand Down Expand Up @@ -211,7 +211,19 @@ import {
DeclarationCoverage,
FileCoverage,
StatementCoverage,
TestCoverageCount
TestCoverageCount,
ChatRequestTurn,
ChatResponseTurn,
ChatResponseAnchorPart,
ChatResponseCommandButtonPart,
ChatResponseFileTreePart,
ChatResponseMarkdownPart,
ChatResponseProgressPart,
ChatResponseReferencePart,
ChatResultFeedbackKind,
LanguageModelChatMessage,
LanguageModelChatMessageRole,
LanguageModelError
} from './types-impl';
import { AuthenticationExtImpl } from './authentication-ext';
import { SymbolKind } from '../common/plugin-api-rpc-model';
Expand Down Expand Up @@ -1223,9 +1235,27 @@ export function createAPIFactory(
/** @stubbed MappedEditsProvider */
registerMappedEditsProvider(documentSelector: theia.DocumentSelector, provider: theia.MappedEditsProvider): Disposable {
return Disposable.NULL;
},
/** @stubbed ChatRequestHandler */
createChatParticipant(id: string, handler: theia.ChatRequestHandler): theia.ChatParticipant {
return {
id,
requestHandler: (request: theia.ChatRequest, context: theia.ChatContext, response: theia.ChatResponseStream, token: CancellationToken) => { },
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just assign the passed-in request handler instead of a dummy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in new commit

dispose() {},
onDidReceiveFeedback: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables)
};
}
};

const lm: typeof theia.lm = {
/** @stubbed LanguageModelChat */
selectChatModels(selector?: theia.LanguageModelChatSelector): Thenable<theia.LanguageModelChat[]> {
return Promise.resolve([]);
},
/** @stubbed LanguageModelChat */
onDidChangeChatModels: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables)
};

return <typeof theia>{
version: require('../../package.json').version,
authentication,
Expand All @@ -1244,6 +1274,7 @@ export function createAPIFactory(
notebooks,
l10n,
tests,
lm,
// Types
StatusBarAlignment: StatusBarAlignment,
Disposable: Disposable,
Expand Down Expand Up @@ -1426,7 +1457,19 @@ export function createAPIFactory(
DeclarationCoverage,
FileCoverage,
StatementCoverage,
TestCoverageCount
TestCoverageCount,
ChatRequestTurn,
ChatResponseTurn,
ChatResponseAnchorPart,
ChatResponseCommandButtonPart,
ChatResponseFileTreePart,
ChatResponseMarkdownPart,
ChatResponseProgressPart,
ChatResponseReferencePart,
ChatResultFeedbackKind,
LanguageModelChatMessage,
LanguageModelChatMessageRole,
LanguageModelError
};
};
}
Expand Down
11 changes: 9 additions & 2 deletions packages/plugin-ext/src/plugin/plugin-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { PreferenceRegistryExtImpl } from './preference-registry';
import { InternalStorageExt, Memento, GlobalState } from './plugin-storage';
import { ExtPluginApi } from '../common/plugin-ext-api-contribution';
import { RPCProtocol } from '../common/rpc-protocol';
import { Emitter } from '@theia/core/lib/common/event';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { WebviewsExtImpl } from './webviews';
import { URI as Uri } from './types-impl';
import { InternalSecretsExt, SecretStorageExt } from '../plugin/secrets-ext';
Expand Down Expand Up @@ -389,7 +389,14 @@ export abstract class AbstractPluginManagerExtImpl<P extends Record<string, any>
environmentVariableCollection: this.terminalService.getEnvironmentVariableCollection(plugin.model.id),
extensionMode: extensionModeValue,
extension,
logUri: Uri.file(logPath)
logUri: Uri.file(logPath),
languageModelAccessInformation: {
/** @stubbed LanguageModelChat */
onDidChange: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables),
canSendRequest(chat: theia.LanguageModelChat): boolean | undefined {
return undefined;
}
}
};
this.pluginContextsMap.set(plugin.model.id, pluginContext);

Expand Down
96 changes: 96 additions & 0 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3846,3 +3846,99 @@ export class TerminalQuickFixOpener {
constructor(uri: theia.Uri) { }
}

// #region Chat
export class ChatRequestTurn {
readonly prompt: string;
readonly participant: string;
readonly command?: string;
readonly references: theia.ChatPromptReference[];
private constructor(prompt: string, command: string | undefined, references: theia.ChatPromptReference[], participant: string) {
this.prompt = prompt;
this.command = command;
this.participant = participant;
this.references = references;
};
}

export class ChatResponseTurn {
readonly command?: string;

private constructor(public readonly response: ReadonlyArray<theia.ChatResponseMarkdownPart | theia.ChatResponseFileTreePart | theia.ChatResponseAnchorPart
| theia.ChatResponseCommandButtonPart>, public readonly result: theia.ChatResult, public readonly participant: string) { }
Copy link
Contributor

Choose a reason for hiding this comment

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

public is not necessary, IMO. Goes for all similar occurrences.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should be addressed in the new commit.

}

export class ChatResponseAnchorPart {
constructor(public value: URI | Location, public title?: string) { }
}

export class ChatResponseProgressPart {
constructor(public value: string) { }
}

export class ChatResponseReferencePart {
constructor(public value: URI | theia.Location, public iconPath?: URI | ThemeIcon | {
light: URI;
dark: URI;
}) { }
}
export class ChatResponseCommandButtonPart {
constructor(public value: theia.Command) { }
}

export class ChatResponseMarkdownPart {
constructor(public value: string | theia.MarkdownString) { }
}

export class ChatResponseFileTreePart {
constructor(public value: theia.ChatResponseFileTree[], public baseUri: URI) { }
}

export type ChatResponsePart = ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart
| ChatResponseProgressPart | ChatResponseReferencePart | ChatResponseCommandButtonPart;

export enum ChatResultFeedbackKind {
Unhelpful = 0,
Helpful = 1,
}

export enum LanguageModelChatMessageRole {
User = 1,
Assistant = 2
}

export class LanguageModelChatMessage {

static User(content: string, name?: string): LanguageModelChatMessage {
return new LanguageModelChatMessage(LanguageModelChatMessageRole.User, content, name);
}

static Assistant(content: string, name?: string): LanguageModelChatMessage {
return new LanguageModelChatMessage(LanguageModelChatMessageRole.Assistant, content, name);
}

constructor(public role: LanguageModelChatMessageRole, public content: string, public name?: string) { }
}

export class LanguageModelError extends Error {

static NoPermissions(message?: string): LanguageModelError {
return new LanguageModelError(message, LanguageModelError.NotFound.name);
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems strange when reading (https://code.visualstudio.com/api/references/vscode-api#LanguageModelError). What should the Error.name and code properties be when we construct a LanguageModelError with the NoPermissions static method?Right now, it would becode === NotFoundandname === LanguageModelError, right? What am I missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I fixed this part. The LanguageModelError NoPermissions/NotFound/Blocked should now have the correct code.

BTW, due to Typescript version used, the 'cause' property used in VS code in the constructor is not available yet (es2022). Is there a way to track these issues, as we do for the monaco uplift annotations? should we introduce a typescript-uplift annotation?

Copy link
Contributor

Choose a reason for hiding this comment

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

I doubt this is an issue of the typescript version: we are using 5.4 at the moment, which is fairly new. I figure the problem is with the compilation target, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, the problem is the target

}

static Blocked(message?: string): LanguageModelError {
return new LanguageModelError(message);
}

static NotFound(message?: string): LanguageModelError {
return new LanguageModelError(message);
}

readonly code: string;

constructor(message?: string, code?: string) {
super(message);
this.name = 'LanguageModelError';
this.code = code ?? '';
}
}
// #endregion
7 changes: 7 additions & 0 deletions packages/plugin/src/theia-extra.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,13 @@ export module '@theia/plugin' {
* see - workspace.fs for how to read and write files and folders from an uri.
*/
readonly logUri: Uri;

/**
* An object that keeps information about how this extension can use language models.
*
* @see {@link LanguageModelChat.sendRequest}
*/
readonly languageModelAccessInformation: LanguageModelAccessInformation;
tsmaeder marked this conversation as resolved.
Show resolved Hide resolved
}

export namespace commands {
Expand Down
Loading
Loading