Skip to content

Commit 2275c7e

Browse files
authoredJan 14, 2025
Merge pull request #2023 from cardstack/run-ai-on-response
Attach tool call results to the call & trigger responses on tool calls
2 parents e6be817 + 258458f commit 2275c7e

File tree

5 files changed

+882
-9
lines changed

5 files changed

+882
-9
lines changed
 

‎packages/ai-bot/helpers.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ export function constructHistory(
156156
cardFragments,
157157
);
158158
}
159-
if (event.type !== 'm.room.message') {
159+
if (
160+
event.type !== 'm.room.message' &&
161+
event.type !== APP_BOXEL_COMMAND_RESULT_EVENT_TYPE
162+
) {
160163
continue;
161164
}
162165
let eventId = event.event_id!;
@@ -595,3 +598,24 @@ export function isCommandResultEvent(
595598
event.content['m.relates_to']?.rel_type === 'm.annotation'
596599
);
597600
}
601+
602+
export function eventRequiresResponse(event: MatrixEvent) {
603+
// If it's a message, we should respond unless it's a card fragment
604+
if (event.getType() === 'm.room.message') {
605+
if (event.getContent().msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE) {
606+
return false;
607+
}
608+
return true;
609+
}
610+
611+
// If it's a command result with output, we should respond
612+
if (
613+
event.getType() === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE &&
614+
event.getContent().msgtype === APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE
615+
) {
616+
return true;
617+
}
618+
619+
// If it's a different type, or a command result without output, we should not respond
620+
return false;
621+
}

‎packages/ai-bot/main.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import {
1414
isCommandResultStatusApplied,
1515
getPromptParts,
1616
extractCardFragmentsFromEvents,
17+
eventRequiresResponse,
1718
} from './helpers';
1819
import {
19-
APP_BOXEL_CARDFRAGMENT_MSGTYPE,
2020
APP_BOXEL_ACTIVE_LLM,
2121
DEFAULT_LLM,
2222
} from '@cardstack/runtime-common/matrix-constants';
@@ -179,12 +179,9 @@ Common issues are:
179179
if (toStartOfTimeline) {
180180
return; // don't print paginated results
181181
}
182-
if (event.getType() !== 'm.room.message') {
182+
if (!eventRequiresResponse(event)) {
183183
return; // only print messages
184184
}
185-
if (event.getContent().msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE) {
186-
return; // don't respond to card fragments, we just gather these in our history
187-
}
188185

189186
if (senderMatrixUserId === aiBotUserId) {
190187
return;

‎packages/ai-bot/tests/prompt-construction-test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,27 @@ test('Tools can be required to be called if done so in the last message', () =>
16111611
});
16121612
});
16131613

1614+
test('Tools calls are connected to their results', () => {
1615+
const eventList: DiscreteMatrixEvent[] = JSON.parse(
1616+
readFileSync(
1617+
path.join(
1618+
__dirname,
1619+
'resources/chats/connect-tool-calls-to-results.json',
1620+
),
1621+
),
1622+
);
1623+
1624+
const { messages } = getPromptParts(eventList, '@aibot:localhost');
1625+
// find the message with the tool call and its id
1626+
// it should have the result deserialised
1627+
const toolCallMessage = messages.find((message) => message.role === 'tool');
1628+
assert.ok(toolCallMessage, 'Should have a tool call message');
1629+
assert.ok(
1630+
toolCallMessage!.content!.includes('Cloudy'),
1631+
'Tool call result should include "Cloudy"',
1632+
);
1633+
});
1634+
16141635
module('set model in prompt', () => {
16151636
test('default active LLM must be equal to `DEFAULT_LLM`', () => {
16161637
const eventList: DiscreteMatrixEvent[] = JSON.parse(

0 commit comments

Comments
 (0)