Skip to content

Commit

Permalink
feat: support inline chat proposed extension api (#3936)
Browse files Browse the repository at this point in the history
* feat: support inline chat extension api

* fix: ci
  • Loading branch information
Ricbet authored Sep 18, 2024
1 parent 36491e3 commit 3dddead
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 2 deletions.
91 changes: 90 additions & 1 deletion packages/extension/src/browser/sumi/main.thread.chat-agents.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
import { Injectable, Injector } from '@opensumi/di';
import { ChatInternalService } from '@opensumi/ide-ai-native/lib/browser/chat/chat.internal.service';
import { ERunStrategy, IInlineChatFeatureRegistry } from '@opensumi/ide-ai-native/lib/browser/types';
import { InlineChatController } from '@opensumi/ide-ai-native/lib/browser/widget/inline-chat/inline-chat-controller';
import {
IChatAgentService,
IChatAgentWelcomeMessage,
IChatFollowup,
IChatReplyFollowup,
} from '@opensumi/ide-ai-native/lib/common';
import { IRPCProtocol } from '@opensumi/ide-connection';
import { Deferred, IChatContent, IChatProgress, IChatTreeData, IMarkdownString } from '@opensumi/ide-core-common';
import {
Deferred,
IChatProgress,
IChatTreeData,
IMarkdownString,
InlineChatFeatureRegistryToken,
} from '@opensumi/ide-core-common';
import { SumiReadableStream } from '@opensumi/ide-utils/lib/stream';

import { ExtHostSumiAPIIdentifier } from '../../common/sumi';
import {
IChatInputParam,
IExtHostChatAgents,
IExtensionChatAgentMetadata,
IInlineChatPreviewProviderMetadata,
IMainThreadChatAgents,
} from '../../common/sumi/chat-agents';

Expand Down Expand Up @@ -46,9 +57,87 @@ export class MainThreadChatAgents implements IMainThreadChatAgents {
}
}

get inlineChatFeatureRegistry(): IInlineChatFeatureRegistry | null {
try {
return this.injector.get(InlineChatFeatureRegistryToken);
} catch (err) {
return null;
}
}

get chatInternalService(): ChatInternalService | null {
try {
return this.injector.get(ChatInternalService);
} catch (err) {
return null;
}
}

constructor(rpcProtocol: IRPCProtocol, private injector: Injector) {
this.#proxy = rpcProtocol.getProxy(ExtHostSumiAPIIdentifier.ExtHostChatAgents);
}
$registerInlineChatProvider(handle: number, name: string, metadata: IInlineChatPreviewProviderMetadata): void {
if (!this.inlineChatFeatureRegistry || !this.chatInternalService) {
return;
}

const d = this.inlineChatFeatureRegistry.registerInteractiveInput(
{ handleStrategy: () => ERunStrategy.PREVIEW },
{
providePreviewStrategy: async (editor, value, token) => {
const controller = new InlineChatController({ enableCodeblockRender: !!metadata.enableCodeblockRender });
const request = this.chatInternalService!.createRequest(value, name)!;

const stream = new SumiReadableStream<IChatProgress>();
controller.mountReadable(stream);

const progressCallback = (progress: IChatProgress) => {
if (token.isCancellationRequested) {
stream.abort();
return;
}

stream.emitData(progress);
};

this.pendingProgress.set(request.requestId, progressCallback);
const requestProps = {
sessionId: this.chatInternalService?.sessionModel.sessionId!,
requestId: request.requestId,
message: request.message.prompt,
command: request.message.command,
};
this.#proxy
.$invokeAgent(handle, requestProps, { history: [] }, token)
.then((result) => {
if (!result) {
stream.end();
return;
}

if (!token.isCancellationRequested) {
if (result.errorDetails) {
request.response.setErrorDetails(result.errorDetails);
}
stream.end();
} else {
stream.abort();
}
})
.finally(() => {
this.pendingProgress.delete(request.requestId);
});

return controller;
},
},
);

this.agents.set(handle, {
name,
dispose: d.dispose,
});
}

$registerAgent(handle: number, name: string, metadata: IExtensionChatAgentMetadata) {
if (!this.chatAgentService) {
Expand Down
12 changes: 12 additions & 0 deletions packages/extension/src/common/sumi/chat-agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export interface IChatInputParam {
prompt: string;
}

/**
* proposed api
*/
export interface IInlineChatPreviewProviderMetadata {
enableCodeblockRender?: boolean;
}

export interface IMainThreadChatAgents {
$registerAgent(handle: number, name: string, metadata: IExtensionChatAgentMetadata): void;
$updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void;
Expand All @@ -38,6 +45,11 @@ export interface IMainThreadChatAgents {
): Promise<number | void>;
$populateChatInput: (handle: number, param: IChatInputParam) => void;
$sendMessage: (chunk: IChatProgress) => void;

/**
* proposed api,会随时更改
*/
$registerInlineChatProvider(handle: number, name: string, metadata: IInlineChatPreviewProviderMetadata): void;
}

export interface IExtHostChatAgents {
Expand Down
31 changes: 30 additions & 1 deletion packages/extension/src/hosted/api/sumi/ext.host.chat.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import { CancellationToken, Emitter, Progress, getDebugLogger, raceCancellation
import { IChatMessage, IChatProgress } from '@opensumi/ide-core-common/lib/types/ai-native';

import { MainThreadSumiAPIIdentifier } from '../../../common/sumi';
import { IChatProgressChunk, IExtHostChatAgents, IMainThreadChatAgents } from '../../../common/sumi/chat-agents';
import {
IChatProgressChunk,
IExtHostChatAgents,
IInlineChatPreviewProviderMetadata,
IMainThreadChatAgents,
} from '../../../common/sumi/chat-agents';
import { IExtensionDescription } from '../../../common/vscode';
import * as typeConverters from '../../../common/vscode/converter';

Expand Down Expand Up @@ -40,6 +45,14 @@ export class ExtHostChatAgents implements IExtHostChatAgents {
return agent.apiAgent;
}

createInlineChatAgent(extension: IExtensionDescription, name: string, handler: sumi.ChatAgentHandler) {
const handle = ExtHostChatAgents.idPool++;
const agent = new ExtHostChatAgent(extension, name, this.proxy, handle, handler);
this.agents.set(handle, agent);
this.proxy.$registerInlineChatProvider(handle, name, {});
return agent.apiInlineChatAgent;
}

sendMessage(extension: IExtensionDescription, chunk: sumi.ChatAgentCustomReplyMessage) {
let messagePayload: IChatProgress | undefined;

Expand Down Expand Up @@ -310,6 +323,18 @@ class ExtHostChatAgent {
};
}

get apiInlineChatAgent(): sumi.InlineChatAgent {
let disposed = false;
const that = this;

return {
dispose() {
disposed = true;
that._proxy.$unregisterAgent(that._handle);
},
};
}

get apiAgent(): sumi.ChatAgent {
let disposed = false;
let updateScheduled = false;
Expand Down Expand Up @@ -434,6 +459,10 @@ export function createChatApiFactory(extension: IExtensionDescription, extHostCh
sendMessage(chunk: sumi.ChatAgentContent) {
return extHostChatAgents.sendMessage(extension, chunk);
},
// proposed api
createInlineChatAgent(name: string, handler: sumi.ChatAgentHandler) {
return extHostChatAgents.createInlineChatAgent(extension, name, handler);
},
};
}

Expand Down
8 changes: 8 additions & 0 deletions packages/types/sumi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,11 @@ declare module 'sumi' {
token: CancellationToken,
) => ProviderResult<ChatAgentResult2>;

// proposed api
export interface InlineChatAgent {
dispose(): void;
}

export interface ChatAgent extends ChatAgent2 {
sampleQuestionProvider?: ChatAgentSampleQuestionProvider;
chatWelcomMessageProvider?: ChatAgentWelcomeMessageProvider;
Expand All @@ -909,5 +914,8 @@ declare module 'sumi' {
export function createChatAgent(name: string, handler: ChatAgentHandler): ChatAgent;

export function sendMessage(chunk: ChatAgentCustomReplyMessage): void;

// proposed api
export function createInlineChatAgent(name: string, handler: ChatAgentHandler): InlineChatAgent;
}
}

1 comment on commit 3dddead

@opensumi
Copy link
Contributor

@opensumi opensumi bot commented on 3dddead Sep 18, 2024

Choose a reason for hiding this comment

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

🎉 Next publish successful!

3.3.4-next-1726639120.0

Please sign in to comment.