feat: allow copilot use without connecting an inbox (#10992)

This PR allows Copilot to be used without connecting the Captain assistant to an inbox. Currently, if Captain is enabled on an account, it takes over conversations and responds directly to users. This PR enables the use of Captain as a Copilot without allowing it to respond to users. Additionally, it allows using a different assistant for Copilot instead of the default Captain assistant.

The selection logic for the Copilot assistant follows this order of preference:

- If the user has selected a specific assistant, it takes first preference for Copilot.
- If the above is not available, the assistant connected to the inbox takes preference.
- If neither of the above is available, the first assistant in the account takes preference.
This commit is contained in:
Vishnu Narayanan
2025-03-01 04:50:39 +05:30
committed by GitHub
parent 24f49b9b5a
commit 616bbced9c
15 changed files with 265 additions and 14 deletions

View File

@@ -489,6 +489,15 @@ const actions = {
commit(types.SET_CONTEXT_MENU_CHAT_ID, chatId);
},
getInboxCaptainAssistantById: async ({ commit }, conversationId) => {
try {
const response = await ConversationApi.getInboxAssistant(conversationId);
commit(types.SET_INBOX_CAPTAIN_ASSISTANT, response.data);
} catch (error) {
// Handle error
}
},
...messageReadActions,
...messageTranslateActions,
};

View File

@@ -108,6 +108,10 @@ const getters = {
getContextMenuChatId: _state => {
return _state.contextMenuChatId;
},
getCopilotAssistant: _state => {
return _state.copilotAssistant;
},
};
export default getters;

View File

@@ -21,6 +21,7 @@ const state = {
conversationLastSeen: null,
syncConversationsMessages: {},
conversationFilters: {},
copilotAssistant: {},
};
// mutations
@@ -309,6 +310,9 @@ export const mutations = {
[types.UPDATE_CHAT_LIST_FILTERS](_state, data) {
_state.conversationFilters = { ..._state.conversationFilters, ...data };
},
[types.SET_INBOX_CAPTAIN_ASSISTANT](_state, data) {
_state.copilotAssistant = data.assistant;
},
};
export default {

View File

@@ -687,4 +687,23 @@ describe('#addMentions', () => {
]);
});
});
describe('#getInboxCaptainAssistantById', () => {
it('fetches inbox assistant by id', async () => {
axios.get.mockResolvedValue({
data: {
id: 1,
name: 'Assistant',
description: 'Assistant description',
},
});
await actions.getInboxCaptainAssistantById({ commit }, 1);
expect(commit.mock.calls).toEqual([
[
types.SET_INBOX_CAPTAIN_ASSISTANT,
{ id: 1, name: 'Assistant', description: 'Assistant description' },
],
]);
});
});
});

View File

@@ -308,4 +308,21 @@ describe('#getters', () => {
});
});
});
describe('#getCopilotAssistant', () => {
it('get copilot assistant', () => {
const state = {
copilotAssistant: {
id: 1,
name: 'Assistant',
description: 'Assistant description',
},
};
expect(getters.getCopilotAssistant(state)).toEqual({
id: 1,
name: 'Assistant',
description: 'Assistant description',
});
});
});
});

View File

@@ -1,3 +1,4 @@
import { describe } from 'vitest';
import types from '../../../mutation-types';
import { mutations } from '../../conversations';
@@ -553,4 +554,19 @@ describe('#mutations', () => {
});
});
});
describe('#SET_INBOX_CAPTAIN_ASSISTANT', () => {
it('set inbox captain assistant', () => {
const state = { copilotAssistant: {} };
const data = {
assistant: {
id: 1,
name: 'Assistant',
description: 'Assistant description',
},
};
mutations[types.SET_INBOX_CAPTAIN_ASSISTANT](state, data);
expect(state.copilotAssistant).toEqual(data.assistant);
});
});
});