mirror of
https://github.com/zulip/zulip.git
synced 2025-11-05 06:23:38 +00:00
settings: Add setting to control permission for topic summarization.
This commit is contained in:
@@ -24,6 +24,10 @@ format used by the Zulip server that they are interacting with.
|
|||||||
|
|
||||||
* [`POST /register`](/api/register-queue): Added
|
* [`POST /register`](/api/register-queue): Added
|
||||||
`server_can_summarize_topics` to the response.
|
`server_can_summarize_topics` to the response.
|
||||||
|
* [`POST /register`](/api/register-queue), [`POST /events`](/api/get-events),
|
||||||
|
`PATCH /realm`: Added `can_summarize_topics_group` realm setting which is
|
||||||
|
a [group-setting value](/api/group-setting-values) describing the set of
|
||||||
|
users with permission to use AI summarization.
|
||||||
|
|
||||||
**Feature level 349**
|
**Feature level 349**
|
||||||
|
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ export function build_page(): void {
|
|||||||
giphy_help_link,
|
giphy_help_link,
|
||||||
...get_realm_level_notification_settings(),
|
...get_realm_level_notification_settings(),
|
||||||
group_setting_labels: settings_config.all_group_setting_labels.realm,
|
group_setting_labels: settings_config.all_group_setting_labels.realm,
|
||||||
|
server_can_summarize_topics: realm.server_can_summarize_topics,
|
||||||
};
|
};
|
||||||
|
|
||||||
const rendered_admin_tab = render_admin_tab(options);
|
const rendered_admin_tab = render_admin_tab(options);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export const realm_group_setting_name_schema = z.enum([
|
|||||||
"can_manage_all_groups",
|
"can_manage_all_groups",
|
||||||
"can_move_messages_between_channels_group",
|
"can_move_messages_between_channels_group",
|
||||||
"can_move_messages_between_topics_group",
|
"can_move_messages_between_topics_group",
|
||||||
|
"can_summarize_topics_group",
|
||||||
"create_multiuse_invite_group",
|
"create_multiuse_invite_group",
|
||||||
"direct_message_initiator_group",
|
"direct_message_initiator_group",
|
||||||
"direct_message_permission_group",
|
"direct_message_permission_group",
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ type TopicPopoverContext = {
|
|||||||
topic_unmuted: boolean;
|
topic_unmuted: boolean;
|
||||||
is_spectator: boolean;
|
is_spectator: boolean;
|
||||||
is_moderator: boolean;
|
is_moderator: boolean;
|
||||||
is_development_environment: boolean;
|
|
||||||
is_topic_empty: boolean;
|
is_topic_empty: boolean;
|
||||||
can_move_topic: boolean;
|
can_move_topic: boolean;
|
||||||
can_rename_topic: boolean;
|
can_rename_topic: boolean;
|
||||||
@@ -67,6 +66,7 @@ type TopicPopoverContext = {
|
|||||||
url: string;
|
url: string;
|
||||||
visibility_policy: number | false;
|
visibility_policy: number | false;
|
||||||
all_visibility_policies: AllVisibilityPolicies;
|
all_visibility_policies: AllVisibilityPolicies;
|
||||||
|
can_summarize_topics: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type VisibilityChangePopoverContext = {
|
type VisibilityChangePopoverContext = {
|
||||||
@@ -267,8 +267,6 @@ export function get_topic_popover_content_context({
|
|||||||
can_move_topic,
|
can_move_topic,
|
||||||
can_rename_topic,
|
can_rename_topic,
|
||||||
is_moderator: current_user.is_moderator,
|
is_moderator: current_user.is_moderator,
|
||||||
// Temporary, as we're using this to control whether we show the summarize popover.
|
|
||||||
is_development_environment: page_params.development_environment,
|
|
||||||
is_realm_admin: current_user.is_admin,
|
is_realm_admin: current_user.is_admin,
|
||||||
topic_is_resolved: resolved_topic.is_resolved(topic_name),
|
topic_is_resolved: resolved_topic.is_resolved(topic_name),
|
||||||
has_starred_messages,
|
has_starred_messages,
|
||||||
@@ -276,6 +274,8 @@ export function get_topic_popover_content_context({
|
|||||||
url,
|
url,
|
||||||
visibility_policy,
|
visibility_policy,
|
||||||
all_visibility_policies,
|
all_visibility_policies,
|
||||||
|
can_summarize_topics:
|
||||||
|
realm.server_can_summarize_topics && settings_data.user_can_summarize_topics(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ export function dispatch_normal_event(event) {
|
|||||||
can_manage_all_groups: noop,
|
can_manage_all_groups: noop,
|
||||||
can_move_messages_between_channels_group: noop,
|
can_move_messages_between_channels_group: noop,
|
||||||
can_move_messages_between_topics_group: noop,
|
can_move_messages_between_topics_group: noop,
|
||||||
|
can_summarize_topics_group: noop,
|
||||||
create_multiuse_invite_group: noop,
|
create_multiuse_invite_group: noop,
|
||||||
default_code_block_language: noop,
|
default_code_block_language: noop,
|
||||||
default_language: noop,
|
default_language: noop,
|
||||||
|
|||||||
@@ -814,6 +814,7 @@ export function check_realm_settings_property_changed(elem: HTMLElement): boolea
|
|||||||
case "realm_can_manage_all_groups":
|
case "realm_can_manage_all_groups":
|
||||||
case "realm_can_move_messages_between_channels_group":
|
case "realm_can_move_messages_between_channels_group":
|
||||||
case "realm_can_move_messages_between_topics_group":
|
case "realm_can_move_messages_between_topics_group":
|
||||||
|
case "realm_can_summarize_topics_group":
|
||||||
case "realm_create_multiuse_invite_group":
|
case "realm_create_multiuse_invite_group":
|
||||||
case "realm_direct_message_initiator_group":
|
case "realm_direct_message_initiator_group":
|
||||||
case "realm_direct_message_permission_group": {
|
case "realm_direct_message_permission_group": {
|
||||||
@@ -1064,6 +1065,7 @@ export function populate_data_for_realm_settings_request(
|
|||||||
"can_invite_users_group",
|
"can_invite_users_group",
|
||||||
"can_move_messages_between_channels_group",
|
"can_move_messages_between_channels_group",
|
||||||
"can_move_messages_between_topics_group",
|
"can_move_messages_between_topics_group",
|
||||||
|
"can_summarize_topics_group",
|
||||||
"create_multiuse_invite_group",
|
"create_multiuse_invite_group",
|
||||||
"direct_message_initiator_group",
|
"direct_message_initiator_group",
|
||||||
"direct_message_permission_group",
|
"direct_message_permission_group",
|
||||||
@@ -1534,6 +1536,7 @@ export const group_setting_widget_map = new Map<string, GroupSettingPillContaine
|
|||||||
["realm_can_manage_all_groups", null],
|
["realm_can_manage_all_groups", null],
|
||||||
["realm_can_move_messages_between_channels_group", null],
|
["realm_can_move_messages_between_channels_group", null],
|
||||||
["realm_can_move_messages_between_topics_group", null],
|
["realm_can_move_messages_between_topics_group", null],
|
||||||
|
["realm_can_summarize_topics_group", null],
|
||||||
["realm_create_multiuse_invite_group", null],
|
["realm_create_multiuse_invite_group", null],
|
||||||
["realm_direct_message_initiator_group", null],
|
["realm_direct_message_initiator_group", null],
|
||||||
["realm_direct_message_permission_group", null],
|
["realm_direct_message_permission_group", null],
|
||||||
|
|||||||
@@ -687,6 +687,7 @@ export const all_group_setting_labels = {
|
|||||||
can_access_all_users_group: $t({
|
can_access_all_users_group: $t({
|
||||||
defaultMessage: "Who can view all other users in the organization",
|
defaultMessage: "Who can view all other users in the organization",
|
||||||
}),
|
}),
|
||||||
|
can_summarize_topics_group: $t({defaultMessage: "Who can use AI summaries"}),
|
||||||
can_create_write_only_bots_group: $t({
|
can_create_write_only_bots_group: $t({
|
||||||
defaultMessage: "Who can create bots that send messages into Zulip",
|
defaultMessage: "Who can create bots that send messages into Zulip",
|
||||||
}),
|
}),
|
||||||
@@ -766,6 +767,7 @@ export const realm_group_permission_settings: {
|
|||||||
subsection_heading: $t({defaultMessage: "Other permissions"}),
|
subsection_heading: $t({defaultMessage: "Other permissions"}),
|
||||||
subsection_key: "org-other-permissions",
|
subsection_key: "org-other-permissions",
|
||||||
settings: [
|
settings: [
|
||||||
|
"can_summarize_topics_group",
|
||||||
"can_create_write_only_bots_group",
|
"can_create_write_only_bots_group",
|
||||||
"can_create_bots_group",
|
"can_create_bots_group",
|
||||||
"can_add_custom_emoji_group",
|
"can_add_custom_emoji_group",
|
||||||
|
|||||||
@@ -90,6 +90,14 @@ export function user_can_create_multiuse_invite(): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function user_can_summarize_topics(): boolean {
|
||||||
|
return user_has_permission_for_group_setting(
|
||||||
|
realm.realm_can_summarize_topics_group,
|
||||||
|
"can_summarize_topics_group",
|
||||||
|
"realm",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function can_subscribe_others_to_all_accessible_streams(): boolean {
|
export function can_subscribe_others_to_all_accessible_streams(): boolean {
|
||||||
return user_has_permission_for_group_setting(
|
return user_has_permission_for_group_setting(
|
||||||
realm.realm_can_add_subscribers_group,
|
realm.realm_can_add_subscribers_group,
|
||||||
|
|||||||
@@ -533,6 +533,7 @@ export function discard_realm_property_element_changes(elem: HTMLElement): void
|
|||||||
case "realm_can_manage_all_groups":
|
case "realm_can_manage_all_groups":
|
||||||
case "realm_can_move_messages_between_channels_group":
|
case "realm_can_move_messages_between_channels_group":
|
||||||
case "realm_can_move_messages_between_topics_group":
|
case "realm_can_move_messages_between_topics_group":
|
||||||
|
case "realm_can_summarize_topics_group":
|
||||||
case "realm_create_multiuse_invite_group":
|
case "realm_create_multiuse_invite_group":
|
||||||
case "realm_direct_message_initiator_group":
|
case "realm_direct_message_initiator_group":
|
||||||
case "realm_direct_message_permission_group": {
|
case "realm_direct_message_permission_group": {
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ export const realm_schema = z.object({
|
|||||||
realm_can_manage_all_groups: group_setting_value_schema,
|
realm_can_manage_all_groups: group_setting_value_schema,
|
||||||
realm_can_move_messages_between_channels_group: group_setting_value_schema,
|
realm_can_move_messages_between_channels_group: group_setting_value_schema,
|
||||||
realm_can_move_messages_between_topics_group: group_setting_value_schema,
|
realm_can_move_messages_between_topics_group: group_setting_value_schema,
|
||||||
|
realm_can_summarize_topics_group: group_setting_value_schema,
|
||||||
realm_create_multiuse_invite_group: group_setting_value_schema,
|
realm_create_multiuse_invite_group: group_setting_value_schema,
|
||||||
realm_date_created: z.number(),
|
realm_date_created: z.number(),
|
||||||
realm_default_code_block_language: z.string(),
|
realm_default_code_block_language: z.string(),
|
||||||
|
|||||||
@@ -506,6 +506,10 @@ input[type="checkbox"] {
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#org-other-permissions .tip {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.language_selection_widget .language_selection_button {
|
.language_selection_widget .language_selection_button {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
{{!-- Group 2 --}}
|
{{!-- Group 2 --}}
|
||||||
<li role="separator" class="popover-menu-separator"></li>
|
<li role="separator" class="popover-menu-separator"></li>
|
||||||
{{#if (and is_development_environment (or is_moderator is_realm_admin))}}
|
{{#if can_summarize_topics}}
|
||||||
<li role="none" class="link-item popover-menu-list-item">
|
<li role="none" class="link-item popover-menu-list-item">
|
||||||
<a role="menuitem" class="sidebar-popover-summarize-topic popover-menu-link" tabindex="0">
|
<a role="menuitem" class="sidebar-popover-summarize-topic popover-menu-link" tabindex="0">
|
||||||
<i class="popover-menu-icon fa fa-magic" aria-hidden="true"></i>
|
<i class="popover-menu-icon fa fa-magic" aria-hidden="true"></i>
|
||||||
|
|||||||
@@ -341,6 +341,15 @@
|
|||||||
{{> settings_save_discard_widget section_name="other-permissions" }}
|
{{> settings_save_discard_widget section_name="other-permissions" }}
|
||||||
</div>
|
</div>
|
||||||
<div class="m-10 inline-block organization-permissions-parent">
|
<div class="m-10 inline-block organization-permissions-parent">
|
||||||
|
{{#unless server_can_summarize_topics}}
|
||||||
|
<div class="tip">
|
||||||
|
{{t "AI summaries are not enabled on this server."}}
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
{{> group_setting_value_pill_input
|
||||||
|
setting_name="realm_can_summarize_topics_group"
|
||||||
|
label=group_setting_labels.can_summarize_topics_group}}
|
||||||
|
|
||||||
{{> group_setting_value_pill_input
|
{{> group_setting_value_pill_input
|
||||||
setting_name="realm_can_create_write_only_bots_group"
|
setting_name="realm_can_create_write_only_bots_group"
|
||||||
label=group_setting_labels.can_create_write_only_bots_group}}
|
label=group_setting_labels.can_create_write_only_bots_group}}
|
||||||
|
|||||||
@@ -632,3 +632,10 @@ run_test("guests_can_access_all_other_users", () => {
|
|||||||
realm.realm_can_access_all_users_group = everyone.id;
|
realm.realm_can_access_all_users_group = everyone.id;
|
||||||
assert.ok(settings_data.guests_can_access_all_other_users());
|
assert.ok(settings_data.guests_can_access_all_other_users());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
run_test("user_can_summarize_topics", () => {
|
||||||
|
test_realm_group_settings(
|
||||||
|
"realm_can_summarize_topics_group",
|
||||||
|
settings_data.user_can_summarize_topics,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ class GroupSettingUpdateData(GroupSettingUpdateDataCore):
|
|||||||
can_manage_all_groups: int | AnonymousSettingGroupDict | None = None
|
can_manage_all_groups: int | AnonymousSettingGroupDict | None = None
|
||||||
can_move_messages_between_channels_group: int | AnonymousSettingGroupDict | None = None
|
can_move_messages_between_channels_group: int | AnonymousSettingGroupDict | None = None
|
||||||
can_move_messages_between_topics_group: int | AnonymousSettingGroupDict | None = None
|
can_move_messages_between_topics_group: int | AnonymousSettingGroupDict | None = None
|
||||||
|
can_summarize_topics_group: int | AnonymousSettingGroupDict | None = None
|
||||||
direct_message_initiator_group: int | AnonymousSettingGroupDict | None = None
|
direct_message_initiator_group: int | AnonymousSettingGroupDict | None = None
|
||||||
direct_message_permission_group: int | AnonymousSettingGroupDict | None = None
|
direct_message_permission_group: int | AnonymousSettingGroupDict | None = None
|
||||||
|
|
||||||
|
|||||||
23
zerver/migrations/0664_realm_can_summarize_topics_group.py
Normal file
23
zerver/migrations/0664_realm_can_summarize_topics_group.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.0.10 on 2025-02-10 10:31
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0663_realm_enable_guest_user_dm_warning"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="realm",
|
||||||
|
name="can_summarize_topics_group",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.RESTRICT,
|
||||||
|
related_name="+",
|
||||||
|
to="zerver.usergroup",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Generated by Django 5.0.10 on 2025-02-10 10:31
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
from django.db.migrations.state import StateApps
|
||||||
|
from django.db.models import OuterRef
|
||||||
|
|
||||||
|
|
||||||
|
def set_default_for_can_summarize_topics_group(
|
||||||
|
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
|
||||||
|
) -> None:
|
||||||
|
Realm = apps.get_model("zerver", "Realm")
|
||||||
|
NamedUserGroup = apps.get_model("zerver", "NamedUserGroup")
|
||||||
|
|
||||||
|
Realm.objects.filter(can_summarize_topics_group=None).update(
|
||||||
|
can_summarize_topics_group=NamedUserGroup.objects.filter(
|
||||||
|
name="role:everyone", realm=OuterRef("id"), is_system_group=True
|
||||||
|
).values("pk")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
atomic = False
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0664_realm_can_summarize_topics_group"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(
|
||||||
|
set_default_for_can_summarize_topics_group,
|
||||||
|
elidable=True,
|
||||||
|
reverse_code=migrations.RunPython.noop,
|
||||||
|
)
|
||||||
|
]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.0.10 on 2025-02-10 10:36
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0665_set_default_for_can_summarize_topics_group"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="realm",
|
||||||
|
name="can_summarize_topics_group",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.RESTRICT,
|
||||||
|
related_name="+",
|
||||||
|
to="zerver.usergroup",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -293,6 +293,11 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||||||
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# UserGroup whose members are allowed to summarize topics.
|
||||||
|
can_summarize_topics_group = models.ForeignKey(
|
||||||
|
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
||||||
|
)
|
||||||
|
|
||||||
# UserGroup whose members are allowed to create invite link.
|
# UserGroup whose members are allowed to create invite link.
|
||||||
create_multiuse_invite_group = models.ForeignKey(
|
create_multiuse_invite_group = models.ForeignKey(
|
||||||
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
||||||
@@ -790,6 +795,13 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||||||
allow_everyone_group=True,
|
allow_everyone_group=True,
|
||||||
default_group_name=SystemGroups.EVERYONE,
|
default_group_name=SystemGroups.EVERYONE,
|
||||||
),
|
),
|
||||||
|
can_summarize_topics_group=GroupPermissionSetting(
|
||||||
|
require_system_group=False,
|
||||||
|
allow_internet_group=False,
|
||||||
|
allow_nobody_group=True,
|
||||||
|
allow_everyone_group=True,
|
||||||
|
default_group_name=SystemGroups.EVERYONE,
|
||||||
|
),
|
||||||
direct_message_initiator_group=GroupPermissionSetting(
|
direct_message_initiator_group=GroupPermissionSetting(
|
||||||
require_system_group=False,
|
require_system_group=False,
|
||||||
allow_internet_group=False,
|
allow_internet_group=False,
|
||||||
@@ -1203,6 +1215,8 @@ def get_realm_with_settings(realm_id: int) -> Realm:
|
|||||||
"can_move_messages_between_channels_group__named_user_group",
|
"can_move_messages_between_channels_group__named_user_group",
|
||||||
"can_move_messages_between_topics_group",
|
"can_move_messages_between_topics_group",
|
||||||
"can_move_messages_between_topics_group__named_user_group",
|
"can_move_messages_between_topics_group__named_user_group",
|
||||||
|
"can_summarize_topics_group",
|
||||||
|
"can_summarize_topics_group__named_user_group",
|
||||||
"direct_message_initiator_group",
|
"direct_message_initiator_group",
|
||||||
"direct_message_initiator_group__named_user_group",
|
"direct_message_initiator_group__named_user_group",
|
||||||
"direct_message_permission_group",
|
"direct_message_permission_group",
|
||||||
|
|||||||
@@ -873,6 +873,9 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings):
|
|||||||
def can_delete_own_message(self) -> bool:
|
def can_delete_own_message(self) -> bool:
|
||||||
return self.has_permission("can_delete_own_message_group")
|
return self.has_permission("can_delete_own_message_group")
|
||||||
|
|
||||||
|
def can_summarize_topics(self) -> bool:
|
||||||
|
return self.has_permission("can_summarize_topics_group")
|
||||||
|
|
||||||
def can_access_public_streams(self) -> bool:
|
def can_access_public_streams(self) -> bool:
|
||||||
return not (self.is_guest or self.realm.is_zephyr_mirror_realm)
|
return not (self.is_guest or self.realm.is_zephyr_mirror_realm)
|
||||||
|
|
||||||
|
|||||||
@@ -4647,6 +4647,14 @@ paths:
|
|||||||
create and edit user groups.
|
create and edit user groups.
|
||||||
|
|
||||||
[calc-full-member]: /api/roles-and-permissions#determining-if-a-user-is-a-full-member
|
[calc-full-member]: /api/roles-and-permissions#determining-if-a-user-is-a-full-member
|
||||||
|
can_summarize_topics_group:
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/components/schemas/GroupSettingValue"
|
||||||
|
- description: |
|
||||||
|
A [group-setting value](/api/group-setting-values) defining the
|
||||||
|
set of users who are allowed to use AI summarization.
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 10.0 (feature level 350).
|
||||||
create_multiuse_invite_group:
|
create_multiuse_invite_group:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/GroupSettingValue"
|
- $ref: "#/components/schemas/GroupSettingValue"
|
||||||
@@ -17662,6 +17670,14 @@ paths:
|
|||||||
to be of type integer and did not accept anonymous user groups.
|
to be of type integer and did not accept anonymous user groups.
|
||||||
|
|
||||||
New in Zulip 8.0 (feature level 225).
|
New in Zulip 8.0 (feature level 225).
|
||||||
|
realm_can_summarize_topics_group:
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/components/schemas/GroupSettingValue"
|
||||||
|
- description: |
|
||||||
|
A [group-setting value](/api/group-setting-values) defining the
|
||||||
|
set of users who are allowed to use AI summarization.
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 10.0 (feature level 350).
|
||||||
zulip_plan_is_not_limited:
|
zulip_plan_is_not_limited:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: |
|
description: |
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ class HomeTest(ZulipTestCase):
|
|||||||
"realm_can_manage_all_groups",
|
"realm_can_manage_all_groups",
|
||||||
"realm_can_move_messages_between_channels_group",
|
"realm_can_move_messages_between_channels_group",
|
||||||
"realm_can_move_messages_between_topics_group",
|
"realm_can_move_messages_between_topics_group",
|
||||||
|
"realm_can_summarize_topics_group",
|
||||||
"realm_create_multiuse_invite_group",
|
"realm_create_multiuse_invite_group",
|
||||||
"realm_create_private_stream_policy",
|
"realm_create_private_stream_policy",
|
||||||
"realm_create_public_stream_policy",
|
"realm_create_public_stream_policy",
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ from django.conf import settings
|
|||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
from analytics.models import UserCount
|
from analytics.models import UserCount
|
||||||
|
from zerver.actions.realm_settings import do_change_realm_permission_group_setting
|
||||||
from zerver.lib.test_classes import ZulipTestCase
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
|
from zerver.models import NamedUserGroup
|
||||||
|
from zerver.models.groups import SystemGroups
|
||||||
|
from zerver.models.realms import get_realm
|
||||||
|
|
||||||
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
|
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
|
||||||
warnings.filterwarnings("ignore", category=DeprecationWarning, module="pydantic")
|
warnings.filterwarnings("ignore", category=DeprecationWarning, module="pydantic")
|
||||||
@@ -119,3 +123,82 @@ class MessagesSummaryTestCase(ZulipTestCase):
|
|||||||
):
|
):
|
||||||
response = self.client_get("/json/messages/summary")
|
response = self.client_get("/json/messages/summary")
|
||||||
self.assert_json_error_contains(response, "Reached monthly limit for AI credits.")
|
self.assert_json_error_contains(response, "Reached monthly limit for AI credits.")
|
||||||
|
|
||||||
|
def test_permission_to_summarize_message_in_topics(self) -> None:
|
||||||
|
narrow = orjson.dumps([["channel", self.channel_name], ["topic", self.topic_name]]).decode()
|
||||||
|
|
||||||
|
realm = get_realm("zulip")
|
||||||
|
moderators_group = NamedUserGroup.objects.get(
|
||||||
|
name=SystemGroups.MODERATORS, realm=realm, is_system_group=True
|
||||||
|
)
|
||||||
|
|
||||||
|
do_change_realm_permission_group_setting(
|
||||||
|
realm,
|
||||||
|
"can_summarize_topics_group",
|
||||||
|
moderators_group,
|
||||||
|
acting_user=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
# In this code path, we test using the fixtures.
|
||||||
|
with open(LLM_FIXTURES_FILE, "rb") as f:
|
||||||
|
fixture_data = orjson.loads(f.read())
|
||||||
|
|
||||||
|
def check_message_summary_permission(user: str, expect_fail: bool = False) -> None:
|
||||||
|
self.login(user)
|
||||||
|
with (
|
||||||
|
self.settings(
|
||||||
|
TOPIC_SUMMARIZATION_MODEL="groq/llama-3.3-70b-versatile",
|
||||||
|
TOPIC_SUMMARIZATION_API_KEY="test",
|
||||||
|
),
|
||||||
|
mock.patch("litellm.completion", return_value=fixture_data["response"]),
|
||||||
|
):
|
||||||
|
result = self.client_get("/json/messages/summary", dict(narrow=narrow))
|
||||||
|
|
||||||
|
if expect_fail:
|
||||||
|
self.assert_json_error(result, "Insufficient permission")
|
||||||
|
else:
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
|
check_message_summary_permission("hamlet", expect_fail=True)
|
||||||
|
check_message_summary_permission("shiva")
|
||||||
|
|
||||||
|
nobody_group = NamedUserGroup.objects.get(
|
||||||
|
name=SystemGroups.NOBODY, realm=realm, is_system_group=True
|
||||||
|
)
|
||||||
|
do_change_realm_permission_group_setting(
|
||||||
|
realm,
|
||||||
|
"can_summarize_topics_group",
|
||||||
|
nobody_group,
|
||||||
|
acting_user=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
check_message_summary_permission("desdemona", expect_fail=True)
|
||||||
|
|
||||||
|
hamletcharacters_group = NamedUserGroup.objects.get(name="hamletcharacters", realm=realm)
|
||||||
|
do_change_realm_permission_group_setting(
|
||||||
|
realm,
|
||||||
|
"can_summarize_topics_group",
|
||||||
|
hamletcharacters_group,
|
||||||
|
acting_user=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
check_message_summary_permission("desdemona", expect_fail=True)
|
||||||
|
check_message_summary_permission("othello", expect_fail=True)
|
||||||
|
check_message_summary_permission("hamlet")
|
||||||
|
check_message_summary_permission("cordelia")
|
||||||
|
|
||||||
|
setting_group = self.create_or_update_anonymous_group_for_setting(
|
||||||
|
[self.example_user("othello")], [moderators_group]
|
||||||
|
)
|
||||||
|
do_change_realm_permission_group_setting(
|
||||||
|
realm,
|
||||||
|
"can_summarize_topics_group",
|
||||||
|
setting_group,
|
||||||
|
acting_user=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
check_message_summary_permission("cordelia", expect_fail=True)
|
||||||
|
check_message_summary_permission("hamlet", expect_fail=True)
|
||||||
|
check_message_summary_permission("othello")
|
||||||
|
check_message_summary_permission("shiva")
|
||||||
|
check_message_summary_permission("desdemona")
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ def get_messages_summary(
|
|||||||
if settings.TOPIC_SUMMARIZATION_MODEL is None: # nocoverage
|
if settings.TOPIC_SUMMARIZATION_MODEL is None: # nocoverage
|
||||||
raise JsonableError(_("AI features are not enabled on this server."))
|
raise JsonableError(_("AI features are not enabled on this server."))
|
||||||
|
|
||||||
if not (user_profile.is_moderator or user_profile.is_realm_admin): # nocoverage
|
if not user_profile.can_summarize_topics():
|
||||||
return json_success(request, {"summary": "Feature limited to moderators for now."})
|
raise JsonableError(_("Insufficient permission"))
|
||||||
|
|
||||||
if settings.MAX_PER_USER_MONTHLY_AI_COST is not None:
|
if settings.MAX_PER_USER_MONTHLY_AI_COST is not None:
|
||||||
used_credits = COUNT_STATS["ai_credit_usage::day"].current_month_accumulated_count_for_user(
|
used_credits = COUNT_STATS["ai_credit_usage::day"].current_month_accumulated_count_for_user(
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ def update_realm(
|
|||||||
can_manage_all_groups: Json[GroupSettingChangeRequest] | None = None,
|
can_manage_all_groups: Json[GroupSettingChangeRequest] | None = None,
|
||||||
can_move_messages_between_channels_group: Json[GroupSettingChangeRequest] | None = None,
|
can_move_messages_between_channels_group: Json[GroupSettingChangeRequest] | None = None,
|
||||||
can_move_messages_between_topics_group: Json[GroupSettingChangeRequest] | None = None,
|
can_move_messages_between_topics_group: Json[GroupSettingChangeRequest] | None = None,
|
||||||
|
can_summarize_topics_group: Json[GroupSettingChangeRequest] | None = None,
|
||||||
direct_message_initiator_group: Json[GroupSettingChangeRequest] | None = None,
|
direct_message_initiator_group: Json[GroupSettingChangeRequest] | None = None,
|
||||||
direct_message_permission_group: Json[GroupSettingChangeRequest] | None = None,
|
direct_message_permission_group: Json[GroupSettingChangeRequest] | None = None,
|
||||||
wildcard_mention_policy: Json[WildcardMentionPolicyEnum] | None = None,
|
wildcard_mention_policy: Json[WildcardMentionPolicyEnum] | None = None,
|
||||||
|
|||||||
Reference in New Issue
Block a user