Skip to content
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 of this.actions) {
if (action?.destructive !== false) {
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)
Comment thread
marcelklehr marked this conversation as resolved.
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