Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 83 additions & 1 deletion src/components/ChattyLLM/AgencyConfirmation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcNoteCard type="info"
<NcNoteCard :type="destructive.destructive ? 'warning' : 'info'"
class="agency-confirmation">
<div class="notecard-content">
<span>
{{ t('assistant', 'The Assistant wants to perform sensitive actions on your behalf.') }}
</span>
<AgencyActions :actions="actions" />
<div class="footer">
<NcButton variant="tertiary"
:title="radiusInfo.description + (destructive.destructive ? '\n' : '') + destructive.description"
:text="radiusInfo.label"
class="help radius">
<template #icon>
<component :is="radiusInfo.icon" :size="20" />
</template>
</NcButton>
<NcButton variant="tertiary"
:title="hint"
class="help">
Expand Down Expand Up @@ -40,13 +48,50 @@
<script>
import InformationOutlineIcon from 'vue-material-design-icons/InformationOutline.vue'
import CloseIcon from 'vue-material-design-icons/Close.vue'
import AccountOutlineIcon from 'vue-material-design-icons/AccountOutline.vue'
import AccountMultipleOutlineIcon from 'vue-material-design-icons/AccountMultipleOutline.vue'
import AccountGroupOutlineIcon from 'vue-material-design-icons/AccountGroupOutline.vue'
import EarthIcon from 'vue-material-design-icons/Earth.vue'
import AssistantIcon from '../icons/AssistantIcon.vue'

import AgencyActions from './AgencyActions.vue'

import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import NcButton from '@nextcloud/vue/components/NcButton'

const radii = {
self: {
icon: AccountOutlineIcon,
// TRANSLATORS Label for AI agent actions whose "impulse radius", or action scope, affects only oneself
label: t('assistant', 'Self'),
// TRANSLATORS Description for AI agent actions whose "impulse radius", or action scope, affects only oneself
description: t('assistant', 'These actions only affect you.'),
},
individuals: {
icon: AccountMultipleOutlineIcon,
// TRANSLATORS Label for AI agent actions whose "impulse radius", or action scope, affects specific individuals
label: t('assistant', 'Individuals'),
// TRANSLATORS Description for AI agent actions whose "impulse radius", or action scope, affects specific individuals
description: t('assistant', 'These actions affect specific other people.'),
},
group: {
icon: AccountGroupOutlineIcon,
// TRANSLATORS Label for AI agent actions whose "impulse radius", or action scope, affects groups of users
label: t('assistant', 'Group'),
// TRANSLATORS Description for AI agent actions whose "impulse radius", or action scope, affects groups of users
description: t('assistant', 'These actions affect a group or team of people.'),
},
external: {
icon: EarthIcon,
// TRANSLATORS Label for AI agent actions whose "impulse radius", or action scope, extends beyond Nextcloud
label: t('assistant', 'External'),
// TRANSLATORS Description for AI agent actions whose "impulse radius", or action scope, extends beyond Nextcloud
description: t('assistant', 'These actions affect people or services outside of this Nextcloud instance.'),
},
}

const radiusOrder = Object.keys(radii)

export default {
name: 'AgencyConfirmation',

Expand All @@ -56,6 +101,10 @@ export default {
NcNoteCard,
NcButton,
CloseIcon,
AccountOutlineIcon,
AccountMultipleOutlineIcon,
AccountGroupOutlineIcon,
EarthIcon,
InformationOutlineIcon,
},

Expand All @@ -76,6 +125,35 @@ export default {
hint: t('assistant', 'If you are not satisfied with the actions the Assistant wants to run, you can adjust your request by sending a new message instead of clicking the "Cancel" button.'),
}
},

computed: {
destructive() {
// determine if any of the pending actions are destructive
// for actions whose destructive state is undefined or unknown, assume they are destructive
for (const action in this.actions) {
if (typeof action.destructive !== 'number' || !!action.destructive) {
return {
destructive: true,
// TRANSLATORS Additional description for destructive AI agent actions
description: t('assistant', 'Some content may be lost.'),
}
}
}
return {
destructive: false,
description: '',
}
},
radiusInfo() {
// show the largest radius among all actions
// for actions with an undefined or unknown radius, assume they are external
const largest = this.actions.reduce((max, action) => {
const index = radiusOrder.indexOf(action?.impulse_radius)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are assuming the assistant PHP API will just give you impulse_radius and destructive from /check_generation, but without making any changes there I don't see how this should be the case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there another way to retrieve that info from the tools? I haven't seen anything else so far.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check my PR. Impulse radius and destrucive attributes of a context agent interaction task are returned here: https://github.com/nextcloud/context_agent/pull/236/changes#diff-305e63f17441090583e8f7affee7120e17c3017bfcd1eb38b27150af3707bf06R297-R308

We need to store these in the database in assistant, likely somewhere around here: https://github.com/nextcloud/assistant/blob/main/lib/Listener/ChattyLLMTaskListener.php#L156-L161. We cannot change the content of the 'actions' output, because people may still be using old versions of assistant.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it looks like pending actions are stored in the DB (Session table) as a JSON string, so we don't have to modify the DB at all. And the only endpoints where I see the actions string is decoded are /check_generation and /check_session. I'm not sure what other solutions there are other than storing impulse_radius and destructive within the pendingActions string in this table.

return Math.max(max, index === -1 ? radiusOrder.length - 1 : index)
}, 0)
return radii[radiusOrder[largest]]
},
},
}
</script>

Expand All @@ -98,6 +176,10 @@ export default {
gap: 4px;
align-items: center;
justify-content: end;

.radius {
margin-right: auto;
}
}
}
</style>
Loading