+
+
+
+
+
+
+
+
import { useAdminApi } from "~/composables/api";
+import { useGroups } from "~/composables/use-groups";
import { alert } from "~/composables/use-toast";
+import type { AIProviderSummary } from "~/lib/api/types/group";
definePageMeta({
layout: "admin",
@@ -106,10 +139,24 @@ const uploadedImage = ref();
const uploadedImageName = ref("");
const uploadedImagePreviewUrl = ref();
-function uploadImage(fileObject: File) {
- uploadedImage.value = fileObject;
- uploadedImageName.value = fileObject.name;
- uploadedImagePreviewUrl.value = URL.createObjectURL(fileObject);
+// Group + provider selection
+const { groups } = useGroups();
+const selectedGroupId = ref(null);
+const groupProviders = ref([]);
+const selectedProviderId = ref(null);
+
+watch(selectedGroupId, (id) => {
+ groupProviders.value = [];
+ selectedProviderId.value = null;
+ if (!id) return;
+ const group = groups.value?.find(g => g.id === id);
+ groupProviders.value = group?.aiProviderSettings?.providers ?? [];
+});
+
+function uploadImage(fileObject: unknown) {
+ uploadedImage.value = fileObject as File;
+ uploadedImageName.value = (fileObject as File).name;
+ uploadedImagePreviewUrl.value = URL.createObjectURL(fileObject as File);
}
function clearImage() {
@@ -119,10 +166,15 @@ function clearImage() {
}
async function testOpenAI() {
+ if (!selectedProviderId.value) {
+ alert.error("Please select a provider");
+ return;
+ }
+
response.value = "";
loading.value = true;
- const { data } = await api.debug.debugOpenAI(uploadedImage.value);
+ const { data } = await api.debug.debugOpenAI(selectedProviderId.value, uploadedImage.value);
loading.value = false;
if (!data) {
diff --git a/frontend/app/pages/admin/manage/groups/[id].vue b/frontend/app/pages/admin/manage/groups/[id].vue
index 781f8c7d0..61b7a7643 100644
--- a/frontend/app/pages/admin/manage/groups/[id].vue
+++ b/frontend/app/pages/admin/manage/groups/[id].vue
@@ -33,6 +33,13 @@
v-if="group.preferences"
v-model="group.preferences"
/>
+
@@ -50,8 +57,10 @@
diff --git a/frontend/app/pages/admin/setup.vue b/frontend/app/pages/admin/setup.vue
index 795795ecb..87bf27336 100644
--- a/frontend/app/pages/admin/setup.vue
+++ b/frontend/app/pages/admin/setup.vue
@@ -45,6 +45,14 @@
:title="$t('settings.site-settings')"
/>
+
+
+
+
+
+
+ {{ $t('group.ai-provider-settings.ai-providers') }}
+
+
+ {{ $t('group.ai-provider-settings.ai-providers-description') }}
+
+
+
+
+
+
+
+
+
+
@@ -252,7 +297,11 @@ import { useLocales } from "~/composables/use-locales";
import { alert } from "~/composables/use-toast";
import { useUserRegistrationForm } from "~/composables/use-users/user-registration-form";
import { useCommonSettingsForm } from "~/composables/use-setup/common-settings-form";
+import { useGroupSelf } from "~/composables/use-groups";
+import { useAIProviders } from "~/composables/use-ai-providers";
import UserRegistrationForm from "~/components/Domain/User/UserRegistrationForm.vue";
+import GroupAIProviderSettingsEditor from "~/components/Domain/Group/GroupAIProviderSettingsEditor.vue";
+import type { AIProviderCreate, AIProviderUpdate } from "~/lib/api/types/group";
definePageMeta({
layout: "blank",
@@ -267,6 +316,42 @@ const userApi = useUserApi();
const adminApi = useAdminApi();
const groupSlug = computed(() => auth.user.value?.groupSlug);
+
+const { group, actions: groupActions } = useGroupSelf();
+const { createOne, updateOne, deleteOne } = useAIProviders();
+
+async function handleCreateProvider(data: AIProviderCreate) {
+ const result = await createOne(data);
+ if (result.data) {
+ await groupActions.refresh();
+ alert.success(i18n.t("group.ai-provider-settings.provider-created"));
+ }
+ else {
+ alert.error(i18n.t("group.ai-provider-settings.provider-create-failed"));
+ }
+}
+
+async function handleUpdateProvider(id: string, data: AIProviderUpdate) {
+ const result = await updateOne(id, data);
+ if (result.data) {
+ await groupActions.refresh();
+ alert.success(i18n.t("group.ai-provider-settings.provider-updated"));
+ }
+ else {
+ alert.error(i18n.t("group.ai-provider-settings.provider-update-failed"));
+ }
+}
+
+async function handleDeleteProvider(id: string) {
+ const result = await deleteOne(id);
+ if (result.data) {
+ await groupActions.refresh();
+ alert.success(i18n.t("group.ai-provider-settings.provider-deleted"));
+ }
+ else {
+ alert.error(i18n.t("group.ai-provider-settings.provider-delete-failed"));
+ }
+}
const { locale } = useLocales();
const router = useRouter();
const isSubmitting = ref(false);
@@ -281,8 +366,9 @@ enum Pages {
LANDING = 1,
USER_INFO = 2,
PAGE_2 = 3,
- CONFIRM = 4,
- END = 5,
+ AI_PROVIDERS = 4,
+ CONFIRM = 5,
+ END = 6,
}
function getStepperColor(currentPage: Pages, page: Pages) {
@@ -475,6 +561,7 @@ async function submitAll() {
const tasks = [
submitRegistration(),
submitCommonSettings(),
+ groupActions.updateAIProviderSettings(),
];
await Promise.all(tasks);
diff --git a/frontend/app/pages/admin/site-settings.vue b/frontend/app/pages/admin/site-settings.vue
index 791e6838e..db4425893 100644
--- a/frontend/app/pages/admin/site-settings.vue
+++ b/frontend/app/pages/admin/site-settings.vue
@@ -284,7 +284,6 @@ const appConfig = ref({
isUpToDate: false,
ldapReady: false,
oidcReady: false,
- enableOpenai: false,
});
function isLocalHostOrHttps() {
return window.location.hostname === "localhost" || window.location.protocol === "https:";
@@ -351,15 +350,6 @@ const simpleChecks = computed(() => {
color: appConfig.value.oidcReady ? goodColor : warningColor,
icon: appConfig.value.oidcReady ? goodIcon : warningIcon,
},
- {
- id: "openai-ready",
- text: appConfig.value.enableOpenai ? i18n.t("settings.openai-ready") : i18n.t("settings.openai-not-ready"),
- status: appConfig.value.enableOpenai,
- errorText: i18n.t("settings.openai-ready-error-text"),
- successText: i18n.t("settings.openai-ready-success-text"),
- color: appConfig.value.enableOpenai ? goodColor : warningColor,
- icon: appConfig.value.enableOpenai ? goodIcon : warningIcon,
- },
];
return data;
});
diff --git a/frontend/app/pages/g/[groupSlug]/r/create.vue b/frontend/app/pages/g/[groupSlug]/r/create.vue
index 04feb0c49..ab9e8b83c 100644
--- a/frontend/app/pages/g/[groupSlug]/r/create.vue
+++ b/frontend/app/pages/g/[groupSlug]/r/create.vue
@@ -45,6 +45,7 @@