diff --git a/packages/host/app/components/ai-assistant/panel.gts b/packages/host/app/components/ai-assistant/panel.gts index 72e5d81f1b..4691186a65 100644 --- a/packages/host/app/components/ai-assistant/panel.gts +++ b/packages/host/app/components/ai-assistant/panel.gts @@ -24,6 +24,8 @@ import { aiBotUsername } from '@cardstack/runtime-common'; import ENV from '@cardstack/host/config/environment'; +import OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service'; + import AddSkillsToRoomCommand from '../../commands/add-skills-to-room'; import CreateAIAssistantRoomCommand from '../../commands/create-ai-assistant-room'; import { Message } from '../../lib/matrix-classes/message'; @@ -40,6 +42,8 @@ import RenameSession from '../ai-assistant/rename-session'; import Room from '../matrix/room'; import DeleteModal from '../operator-mode/delete-modal'; +import { Submodes } from '../submode-switcher'; + import assistantIcon from './ai-assist-icon.webp'; import type MatrixService from '../../services/matrix-service'; @@ -368,6 +372,7 @@ export default class AiAssistantPanel extends Component { @service private declare monacoService: MonacoService; @service private declare router: RouterService; @service private declare commandService: CommandService; + @service private declare operatorModeStateService: OperatorModeStateService; @tracked private isShowingPastSessions = false; @tracked private roomToRename: SessionRoomData | undefined = undefined; @@ -545,6 +550,9 @@ export default class AiAssistantPanel extends Component { @action private enterRoom(roomId: string, hidePastSessionsList = true) { this.matrixService.currentRoomId = roomId; + if (this.operatorModeStateService.state.submode === Submodes.Code) { + this.matrixService.setLLMModelForCodeMode(); + } if (hidePastSessionsList) { this.hidePastSessions(); } diff --git a/packages/host/app/resources/room.ts b/packages/host/app/resources/room.ts index 792b83d3da..d8a8202f1a 100644 --- a/packages/host/app/resources/room.ts +++ b/packages/host/app/resources/room.ts @@ -228,6 +228,9 @@ export class RoomResource extends Resource { } activateLLM(model: string) { + if (this.activeLLM === model) { + return; + } this._activeLLM = model; this.activateLLMTask.perform(model); } diff --git a/packages/host/app/services/matrix-service.ts b/packages/host/app/services/matrix-service.ts index 2e896d28e3..7b44b9f0fb 100644 --- a/packages/host/app/services/matrix-service.ts +++ b/packages/host/app/services/matrix-service.ts @@ -48,6 +48,7 @@ import { APP_BOXEL_REALMS_EVENT_TYPE, APP_BOXEL_ACTIVE_LLM, LEGACY_APP_BOXEL_REALMS_EVENT_TYPE, + DEFAULT_LLM_LIST, } from '@cardstack/runtime-common/matrix-constants'; import { @@ -1337,6 +1338,25 @@ export default class MatrixService extends Service { await this.client?.scrollback(room); } } + + async setLLMModelForCodeMode() { + this.setLLMModel('anthropic/claude-3.5-sonnet'); + } + + private async setLLMModel(model: string) { + if (!DEFAULT_LLM_LIST.includes(model)) { + throw new Error(`Cannot find LLM model: ${model}`); + } + if (!this.currentRoomId) { + return; + } + let roomResource = this.roomResources.get(this.currentRoomId); + if (!roomResource) { + return; + } + await roomResource.loading; + roomResource.activateLLM(model); + } } function saveAuth(auth: LoginResponse) { diff --git a/packages/host/app/services/operator-mode-state-service.ts b/packages/host/app/services/operator-mode-state-service.ts index 0be9a0fedc..86b4a02dd9 100644 --- a/packages/host/app/services/operator-mode-state-service.ts +++ b/packages/host/app/services/operator-mode-state-service.ts @@ -34,6 +34,7 @@ import { type Stack } from '../components/operator-mode/interact-submode'; import { removeFileExtension } from '../components/search-sheet/utils'; +import MatrixService from './matrix-service'; import NetworkService from './network'; import type CardService from './card-service'; @@ -97,6 +98,7 @@ export default class OperatorModeStateService extends Service { @service private declare router: RouterService; @service private declare reset: ResetService; @service private declare network: NetworkService; + @service private declare matrixService: MatrixService; constructor(owner: Owner) { super(owner); @@ -310,6 +312,10 @@ export default class OperatorModeStateService extends Service { updateSubmode(submode: Submode) { this.state.submode = submode; this.schedulePersist(); + + if (submode === Submodes.Code) { + this.matrixService.setLLMModelForCodeMode(); + } } updateCodePathWithCodeSelection( diff --git a/packages/host/tests/acceptance/ai-assistant-test.gts b/packages/host/tests/acceptance/ai-assistant-test.gts index 9d328594fc..02cd6a6f6e 100644 --- a/packages/host/tests/acceptance/ai-assistant-test.gts +++ b/packages/host/tests/acceptance/ai-assistant-test.gts @@ -1,4 +1,4 @@ -import { click, fillIn } from '@ember/test-helpers'; +import { click, fillIn, waitFor } from '@ember/test-helpers'; import { module, test } from 'qunit'; @@ -326,4 +326,29 @@ module('Acceptance | AI Assistant tests', function (hooks) { let roomState = getRoomState('mock_room_1', APP_BOXEL_ACTIVE_LLM, ''); assert.strictEqual(roomState.model, 'google/gemini-pro-1.5'); }); + + test('defaults to anthropic/claude-3.5-sonnet in code mode', async function (assert) { + await visitOperatorMode({ + stacks: [ + [ + { + id: `${testRealmURL}index`, + format: 'isolated', + }, + ], + ], + }); + + await click('[data-test-submode-switcher] button'); + await click('[data-test-boxel-menu-item-text="Code"]'); + await click('[data-test-open-ai-assistant]'); + assert.dom('[data-test-llm-select-selected]').hasText('claude-3.5-sonnet'); + + createAndJoinRoom('@testuser:staging', 'room-test-2'); + + await click('[data-test-past-sessions-button]'); + await waitFor("[data-test-enter-room='mock_room_2']"); + await click('[data-test-enter-room="mock_room_2"]'); + assert.dom('[data-test-llm-select-selected]').hasText('claude-3.5-sonnet'); + }); });