Skip to content

Commit

Permalink
Merge pull request #2092 from cardstack/cs-7713-add-a-host-command-to…
Browse files Browse the repository at this point in the history
…-change-llm-model-for-a-room

Add a host command to change llm model for a room
  • Loading branch information
lukemelia authored Jan 28, 2025
2 parents 9c9bf9f + cbd747c commit 68873c5
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/base/command.gts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ export class CreateAIAssistantRoomResult extends CardDef {
@field roomId = contains(StringField);
}

export class SetActiveLLMInput extends CardDef {
@field roomId = contains(StringField);
@field model = contains(StringField);
}

export class AddSkillsToRoomInput extends CardDef {
@field roomId = contains(StringField);
@field skills = linksToMany(SkillCard);
Expand Down
210 changes: 210 additions & 0 deletions packages/experiments-realm/set-llm-example.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { CardDef, Component } from 'https://cardstack.com/base/card-api';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { on } from '@ember/modifier';
import SetActiveLLMCommand from '@cardstack/boxel-host/commands/set-active-llm';
import CreateAiAssistantRoomCommand from '@cardstack/boxel-host/commands/create-ai-assistant-room';
import OpenAiAssistantRoomCommand from '@cardstack/boxel-host/commands/open-ai-assistant-room';
import { Button } from '@cardstack/boxel-ui/components';

class IsolatedTemplate extends Component<typeof SetLlmExample> {
@tracked modelId = 'microsoft/phi-4';
@tracked currentRoomId: string | null = null;

@action
async createRoom() {
let commandContext = this.args.context?.commandContext;
if (!commandContext) return;

let createAIAssistantRoomCommand = new CreateAiAssistantRoomCommand(
commandContext,
);
let { roomId } = await createAIAssistantRoomCommand.execute({
name: `Chat with ${this.modelId}`,
});

let openAiAssistantRoomCommand = new OpenAiAssistantRoomCommand(
commandContext,
);
await openAiAssistantRoomCommand.execute({
roomId,
});

this.currentRoomId = roomId;
}

@action
async setLLM() {
if (!this.currentRoomId) return;

let commandContext = this.args.context?.commandContext;
if (!commandContext) return;

let setActiveLLMCommand = new SetActiveLLMCommand(commandContext);

await setActiveLLMCommand.execute({
model: this.modelId,
roomId: this.currentRoomId,
});
}

@action
updatemodelId(event: Event) {
this.modelId = (event.target as HTMLInputElement).value;
}

<template>
<div class='llm-setter'>
<div class='content'>
<div class='input-section'>
<label for='llm-model-name-input'>LLM Model Name</label>
<input
type='text'
value={{this.modelId}}
{{on 'input' this.updatemodelId}}
class='model-input'
id='llm-model-name-input'
/>
</div>

<div class='buttons-section'>
<Button
class='create-button'
data-test-create-room
{{on 'click' this.createRoom}}
>
Create Room
</Button>

<Button
class='set-button'
data-test-set-llm
{{on 'click' this.setLLM}}
>
Set Active LLM
</Button>
</div>

{{#if this.currentRoomId}}
<div class='room-info'>
<span class='room-label'>Room ID:</span>
<span class='room-id'>{{this.currentRoomId}}</span>
</div>
{{/if}}
</div>
</div>
<style scoped>
.llm-setter {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
}
.content {
background: white;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
padding: 1.5rem;
}
.input-section {
margin-bottom: 1.5rem;
}
.input-section label {
display: block;
margin-bottom: 0.5rem;
color: #7f8c8d;
font-size: 0.9rem;
}
.model-input {
width: 100%;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.2s;
}
.model-input:focus {
outline: none;
border-color: #34d399;
box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.2);
}
.buttons-section {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.create-button,
.set-button {
flex: 1;
padding: 0.75rem 1.5rem;
font-size: 1.1rem;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition:
transform 0.2s,
box-shadow 0.2s;
}
.create-button {
background: linear-gradient(135deg, #4f46e5, #3730a3);
}
.set-button {
background: linear-gradient(135deg, #34d399, #059669);
}
.create-button:hover,
.set-button:hover {
transform: translateY(-1px);
}
.create-button:hover {
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
}
.set-button:hover {
box-shadow: 0 4px 12px rgba(52, 211, 153, 0.3);
}
.set-button[disabled] {
opacity: 0.5;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.room-info {
margin-top: 1rem;
padding: 1rem;
background: #f8fafc;
border-radius: 8px;
font-size: 0.9rem;
}
.room-label {
color: #64748b;
margin-right: 0.5rem;
}
.room-id {
color: #1e293b;
font-family: monospace;
font-size: 0.95rem;
}
</style>
</template>
}

export class SetLlmExample extends CardDef {
static displayName = 'SetLLMExample';

static isolated = IsolatedTemplate;
}
5 changes: 5 additions & 0 deletions packages/host/app/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as ReloadCardCommandModule from './reload-card';
import * as SaveCardCommandModule from './save-card';
import * as SearchCardsCommandModule from './search-cards';
import * as SendAiAssistantMessageModule from './send-ai-assistant-message';
import * as SetActiveLlmModule from './set-active-llm';
import * as ShowCardCommandModule from './show-card';
import * as SwitchSubmodeCommandModule from './switch-submode';
import * as UpdateSkillActivationCommandModule from './update-skill-activation';
Expand Down Expand Up @@ -51,6 +52,10 @@ export function shimHostCommands(virtualNetwork: VirtualNetwork) {
'@cardstack/boxel-host/commands/send-ai-assistant-message',
SendAiAssistantMessageModule,
);
virtualNetwork.shimModule(
'@cardstack/boxel-host/commands/set-active-llm',
SetActiveLlmModule,
);
virtualNetwork.shimModule(
'@cardstack/boxel-host/commands/show-card',
ShowCardCommandModule,
Expand Down
27 changes: 27 additions & 0 deletions packages/host/app/commands/set-active-llm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { service } from '@ember/service';

import type * as BaseCommandModule from 'https://cardstack.com/base/command';

import HostBaseCommand from '../lib/host-base-command';

import type MatrixService from '../services/matrix-service';

export default class SetActiveLLMCommand extends HostBaseCommand<
typeof BaseCommandModule.SetActiveLLMInput,
undefined
> {
@service private declare matrixService: MatrixService;

async getInputType() {
let commandModule = await this.loadCommandModule();
const { SetActiveLLMInput } = commandModule;
return SetActiveLLMInput;
}

protected async run(
input: BaseCommandModule.SetActiveLLMInput,
): Promise<undefined> {
await this.matrixService.sendActiveLLMEvent(input.roomId, input.model);
return undefined;
}
}

0 comments on commit 68873c5

Please sign in to comment.