Skip to content

Commit

Permalink
add user setting option for dis/enable agent pinning feature
Browse files Browse the repository at this point in the history
  • Loading branch information
atahankilc committed Feb 7, 2025
1 parent 7f8547d commit cc017fb
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {

bind(AIChatInputWidget).toSelf();
bind(AIChatInputConfiguration).toConstantValue({
showContext: false
showContext: false,
showPinnedAgent: true
});
bind(WidgetFactory).toDynamicValue(({ container }) => ({
id: AIChatInputWidget.ID,
Expand Down
27 changes: 23 additions & 4 deletions packages/ai-chat-ui/src/browser/chat-input-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type DeleteChangeSetElement = (requestModel: ChatRequestModel, index: number) =>
export const AIChatInputConfiguration = Symbol('AIChatInputConfiguration');
export interface AIChatInputConfiguration {
showContext?: boolean;
showPinnedAgent?: boolean;
}

@injectable()
Expand Down Expand Up @@ -126,6 +127,7 @@ export class AIChatInputWidget extends ReactWidget {
this.editorReady.resolve();
}}
showContext={this.configuration?.showContext}
showPinnedAgent={this.configuration?.showPinnedAgent}
labelProvider={this.labelProvider}
/>
);
Expand Down Expand Up @@ -160,6 +162,7 @@ interface ChatInputProperties {
contextMenuCallback: (event: IMouseEvent) => void;
setEditorRef: (editor: MonacoEditor | undefined) => void;
showContext?: boolean;
showPinnedAgent?: boolean;
labelProvider: LabelProvider;
}

Expand Down Expand Up @@ -333,6 +336,22 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
}
};

const handlePin = () => {
if (editorRef.current) {
editorRef.current.getControl().getModel()?.applyEdits([{
range: {
startLineNumber: 1,
startColumn: 1,
endLineNumber: 1,
endColumn: 1
},
text: '@ ',
}]);
editorRef.current.getControl().setPosition({ lineNumber: 1, column: 2 });
editorRef.current.getControl().getAction('editor.action.triggerSuggest')?.run();
}
};

const leftOptions = [
...(props.showContext
? [{
Expand All @@ -341,14 +360,14 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
className: 'codicon-add'
}]
: []),
...(props.pinnedAgent
...(props.showPinnedAgent
? [{
title: 'Unpin Agent',
handler: props.onUnpin,
title: props.pinnedAgent ? 'Unpin Agent' : 'Pin Agent',
handler: props.pinnedAgent ? props.onUnpin : handlePin,
className: 'at-icon',
text: {
align: 'right',
content: props.pinnedAgent.name
content: props.pinnedAgent && props.pinnedAgent.name
},
}]
: []),
Expand Down
4 changes: 3 additions & 1 deletion packages/ai-chat/src/browser/ai-chat-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
ChatRequestParser,
ChatRequestParserImpl,
ChatService,
DefaultChatAgentId
DefaultChatAgentId,
PinChatAgent
} from '../common';
import { ChatAgentsVariableContribution } from '../common/chat-agents-variable-contribution';
import { CommandChatAgent } from '../common/command-chat-agents';
Expand All @@ -51,6 +52,7 @@ export default new ContainerModule(bind => {
bind(ChatAgentServiceImpl).toSelf().inSingletonScope();
bind(ChatAgentService).toService(ChatAgentServiceImpl);
bind(DefaultChatAgentId).toConstantValue({ id: OrchestratorChatAgentId });
bind(PinChatAgent).toConstantValue(true);

bindContributionProvider(bind, ResponseContentMatcherProvider);
bind(DefaultResponseContentMatcherProvider).toSelf().inSingletonScope();
Expand Down
9 changes: 9 additions & 0 deletions packages/ai-chat/src/browser/ai-chat-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-pr
import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution';

export const DEFAULT_CHAT_AGENT_PREF = 'ai-features.chat.defaultChatAgent';
export const PIN_CHAT_AGENT_PREF = 'ai-features.chat.pinChatAgent';

export const aiChatPreferences: PreferenceSchema = {
type: 'object',
Expand All @@ -27,6 +28,14 @@ export const aiChatPreferences: PreferenceSchema = {
description: 'Optional: <agent-name> of the Chat Agent that shall be invoked, if no agent is explicitly mentioned with @<agent-name> in the user query.\
If no Default Agent is configured, Theia´s defaults will be applied.',
title: AI_CORE_PREFERENCES_TITLE,
},
[PIN_CHAT_AGENT_PREF]: {
type: 'boolean',
description: 'Enable agent pinning to automatically keep a mentioned chat agent active across prompts, reducing the need for repeated mentions.\
\n\
You can manually unpin or switch agents anytime.',
default: true,
title: AI_CORE_PREFERENCES_TITLE,
}
}
};
19 changes: 16 additions & 3 deletions packages/ai-chat/src/browser/frontend-chat-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,30 @@
// *****************************************************************************

import { inject, injectable } from '@theia/core/shared/inversify';
import { ChatAgent, ChatServiceImpl, ParsedChatRequest } from '../common';
import { ChatAgent, ChatServiceImpl, ChatSession, ParsedChatRequest } from '../common';
import { PreferenceService } from '@theia/core/lib/browser';
import { DEFAULT_CHAT_AGENT_PREF } from './ai-chat-preferences';
import { DEFAULT_CHAT_AGENT_PREF, PIN_CHAT_AGENT_PREF } from './ai-chat-preferences';

@injectable()
export class FrontendChatServiceImpl extends ChatServiceImpl {

@inject(PreferenceService)
protected preferenceService: PreferenceService;

protected override getAgent(parsedRequest: ParsedChatRequest): ChatAgent | undefined {
protected override getAgent(parsedRequest: ParsedChatRequest, session: ChatSession): ChatAgent | undefined {
let agent = this.initialAgentSelection(parsedRequest);
if (!this.preferenceService.get<boolean>(PIN_CHAT_AGENT_PREF)) {
return agent;
}
if (!session.pinnedAgent && agent && agent.id !== this.defaultChatAgentId?.id) {
session.pinnedAgent = agent;
} else if (session.pinnedAgent && this.getMentionedAgent(parsedRequest) === undefined) {
agent = session.pinnedAgent;
}
return agent;
}

protected override initialAgentSelection(parsedRequest: ParsedChatRequest): ChatAgent | undefined {
const agentPart = this.getMentionedAgent(parsedRequest);
if (agentPart) {
return this.chatAgentService.getAgent(agentPart.agentId);
Expand Down
29 changes: 21 additions & 8 deletions packages/ai-chat/src/common/chat-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export interface DefaultChatAgentId {
id: string;
}

export const PinChatAgent = Symbol('PinChatAgent');
export type PinChatAgent = boolean;

export const ChatService = Symbol('ChatService');
export interface ChatService {
onActiveSessionChanged: Event<ActiveSessionChangedEvent>
Expand Down Expand Up @@ -109,6 +112,9 @@ export class ChatServiceImpl implements ChatService {
@inject(DefaultChatAgentId) @optional()
protected defaultChatAgentId: DefaultChatAgentId | undefined;

@inject(PinChatAgent) @optional()
protected pinChatAgent: boolean | undefined;

@inject(ChatRequestParser)
protected chatRequestParser: ChatRequestParser;

Expand Down Expand Up @@ -167,13 +173,7 @@ export class ChatServiceImpl implements ChatService {
session.title = request.text;

const parsedRequest = this.chatRequestParser.parseChatRequest(request, session.model.location);
let agent = this.getAgent(parsedRequest);

if (!session.pinnedAgent && agent && agent.id !== this.defaultChatAgentId?.id) {
session.pinnedAgent = agent;
} else if (session.pinnedAgent && this.getMentionedAgent(parsedRequest) === undefined) {
agent = session.pinnedAgent;
}
const agent = this.getAgent(parsedRequest, session);

if (agent === undefined) {
const error = 'No ChatAgents available to handle request!';
Expand Down Expand Up @@ -236,7 +236,20 @@ export class ChatServiceImpl implements ChatService {
return this.getSession(sessionId)?.model.getRequest(requestId)?.response.cancel();
}

protected getAgent(parsedRequest: ParsedChatRequest): ChatAgent | undefined {
protected getAgent(parsedRequest: ParsedChatRequest, session: ChatSession): ChatAgent | undefined {
let agent = this.initialAgentSelection(parsedRequest);
if (this.pinChatAgent === false) {
return agent;
}
if (!session.pinnedAgent && agent && agent.id !== this.defaultChatAgentId?.id) {
session.pinnedAgent = agent;
} else if (session.pinnedAgent && this.getMentionedAgent(parsedRequest) === undefined) {
agent = session.pinnedAgent;
}
return agent;
}

protected initialAgentSelection(parsedRequest: ParsedChatRequest): ChatAgent | undefined {
const agentPart = this.getMentionedAgent(parsedRequest);
if (agentPart) {
return this.chatAgentService.getAgent(agentPart.agentId);
Expand Down

0 comments on commit cc017fb

Please sign in to comment.