Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v7.1.2 #9399

Merged
merged 57 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
39d988f
Add Swagger doc to /_api/v3/admin-home/
Oct 31, 2024
17282e8
Add swagger doc to app-settings.js
Nov 1, 2024
fa7049b
description修正
Nov 5, 2024
80f56bb
Add Cookie auth
Nov 5, 2024
9315842
Add Security schema
Nov 5, 2024
37b1bc1
Add AdminHome to definition-apiv3.js
Nov 5, 2024
7fdc289
Add security data
Nov 5, 2024
23dde87
コンフリクトを起こします
Ryosei-Fukushima Nov 7, 2024
4c4c707
Bump version
Nov 12, 2024
451ae2f
Merge pull request #9398 from weseek/support/prepare-v7.1.2-RC.0
mergify[bot] Nov 12, 2024
78af624
Add badger illust
satof3 Nov 13, 2024
7fbbe89
Fix light image
satof3 Nov 13, 2024
94951ad
upgrade @growi/*
yuki-takei Nov 13, 2024
adacf37
fix version-subpackages script
yuki-takei Nov 13, 2024
44321d1
Merge pull request #9401 from weseek/support/upgrade-growi-scope-pack…
mergify[bot] Nov 13, 2024
ba609c2
Merge branch 'master' into support/157164-add-badger-illust-in-huffle…
satof3 Nov 14, 2024
1a50dcc
add message no_unread_messages
Ryosei-Fukushima Nov 14, 2024
37484d0
Merge pull request #9403 from weseek/support/157164-add-badger-illust…
yuki-takei Nov 14, 2024
9f494b2
Merge pull request #9405 from weseek/fix/157375-add-no-unread-messages
mergify[bot] Nov 14, 2024
ab5a111
improve instructions
yuki-takei Nov 14, 2024
57962d7
Merge pull request #9407 from weseek/imprv/knowledge-assistant-instru…
mergify[bot] Nov 14, 2024
a675171
add @types/bunyan
yuki-takei Nov 14, 2024
3395ae8
Fix parameters description
Nov 15, 2024
816038b
Add default security
Nov 15, 2024
dec3bbe
Change type session to apiKey
Nov 15, 2024
74d5626
Update /app-settings/v5-schema-migration to send empty body
Nov 15, 2024
554d0ad
change default value
yuki-takei Nov 15, 2024
08c2f41
allows to configure the model by env var
yuki-takei Nov 15, 2024
d33daee
omit OPENAI_ASSISTANT_NAME_SUFFIX
yuki-takei Nov 15, 2024
b1fb542
Impl sanitizeMarkdownForVectorStoreFile
miya Nov 15, 2024
4d59d78
sanitize-markdown-for-vector-store-file -> sanitize-markdown
miya Nov 15, 2024
8e9c5f5
BugFix
yuki-takei Nov 15, 2024
6fe56ad
revert default value
yuki-takei Nov 15, 2024
293aa60
Merge pull request #9410 from weseek/imprv/knowedge-assistant-model-s…
mergify[bot] Nov 15, 2024
9ada2fe
add isAiEnabled to get config value
yuki-takei Nov 15, 2024
90fa996
refactor barrel file
yuki-takei Nov 15, 2024
e4a25bb
setup router dynamically
yuki-takei Nov 15, 2024
991464c
refactor starting cron services
yuki-takei Nov 15, 2024
2c96011
import openai modules dynamically in page operation
yuki-takei Nov 15, 2024
218337a
revert function declaration
yuki-takei Nov 15, 2024
a742de9
create thead before the first post
yuki-takei Nov 15, 2024
b5df52c
Merge pull request #9414 from weseek/imprv/threads-creation
mergify[bot] Nov 15, 2024
03c0a0d
run CI for app development on pull_request event
yuki-takei Nov 15, 2024
28982d0
Merge pull request #9340 from goofmint/features/swagger-admin-home
yuki-takei Nov 15, 2024
ae126eb
Merge pull request #9360 from goofmint/features/update_swagger_base_v3
yuki-takei Nov 15, 2024
c514f40
Merge pull request #9346 from goofmint/features/swagger-app-settings
yuki-takei Nov 15, 2024
9d06416
Use remark
miya Nov 16, 2024
67c8fe7
Add awati
miya Nov 16, 2024
cc31c71
Change replacement string
miya Nov 17, 2024
1feba06
Performance improvement of dynamic import
miya Nov 17, 2024
0ac74ca
Merge pull request #9413 from weseek/support/import-openai-features-d…
mergify[bot] Nov 18, 2024
25a1bec
Merge pull request #9411 from weseek/feat/157516-remove-unnecessary-s…
mergify[bot] Nov 18, 2024
b6eedb5
update DAYS_UNTIL_EXPIRATION
yuki-takei Nov 18, 2024
af66e0c
update cron interval for fractional sleep
yuki-takei Nov 18, 2024
bb5315b
refactor
yuki-takei Nov 18, 2024
43930d1
add normalize script
yuki-takei Nov 18, 2024
8e2fa75
Merge pull request #9419 from weseek/imprv/shorten-thread-deletion-ex…
mergify[bot] Nov 18, 2024
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
13 changes: 13 additions & 0 deletions .github/workflows/ci-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ on:
- apps/app/**
- '!apps/app/docker/**'
- packages/**
pull_request:
types: [opened, reopened, synchronize]
paths:
- .github/mergify.yml
- .github/workflows/ci-app.yml
- .eslint*
- tsconfig.base.json
- turbo.json
- pnpm-lock.yaml
- package.json
- apps/app/**
- '!apps/app/docker/**'
- packages/**

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
6 changes: 6 additions & 0 deletions apps/app/bin/swagger-jsdoc/definition-apiv3.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ module.exports = {
name: 'access_token',
in: 'query',
},
cookieAuth: {
type: 'apiKey',
in: 'cookie',
name: 'connect.sid',
},
},
},
'x-tagGroups': [
Expand Down Expand Up @@ -57,6 +62,7 @@ module.exports = {
name: 'System Management API',
tags: [
'Home',
'AdminHome',
'AppSettings',
'SecuritySetting',
'MarkDownSetting',
Expand Down
3 changes: 2 additions & 1 deletion apps/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@growi/app",
"version": "7.1.1",
"version": "7.1.2-RC.0",
"license": "MIT",
"private": "true",
"scripts": {
Expand Down Expand Up @@ -257,6 +257,7 @@
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/bunyan": "^1.8.11",
"@types/express": "^4.17.21",
"@types/hast": "^3.0.4",
"@types/jest": "^29.5.2",
Expand Down
1 change: 1 addition & 0 deletions apps/app/public/static/locales/en_US/commons.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"all": "All",
"unopend": "Unread",
"mark_all_as_read": "Mark all as read",
"no_unread_messages": "no_unread_messages",
"only_unread": "Only unread"
},

Expand Down
3 changes: 2 additions & 1 deletion apps/app/public/static/locales/fr_FR/commons.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
"no_notification": "Vous n'avez pas de notifications.",
"all": "Toutes",
"unopend": "Non-lues",
"mark_all_as_read": "Tout marquer comme lu"
"mark_all_as_read": "Tout marquer comme lu",
"no_unread_messages": "aucun message non lu"
},

"personal_dropdown": {
Expand Down
1 change: 1 addition & 0 deletions apps/app/public/static/locales/ja_JP/commons.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"all": "全て",
"unopend": "未読",
"mark_all_as_read": "全て既読にする",
"no_unread_messages": "未読はありません",
"only_unread": "未読のみ"
},

Expand Down
1 change: 1 addition & 0 deletions apps/app/public/static/locales/zh_CN/commons.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"all": "全部",
"unopend": "未读",
"mark_all_as_read" : "标记为已读",
"no_unread_messages": "no_unread_messages",
"only_unread": "Only unread"
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const InAppNotificationDropdown = (): JSX.Element => {
<DropdownMenu end>
{ inAppNotificationData != null && inAppNotificationData.docs.length === 0
// no items
? <DropdownItem disabled>{t('in_app_notification.mark_all_as_read')}</DropdownItem>
? <DropdownItem disabled>{t('in_app_notification.no_unread_messages')}</DropdownItem>
// render DropdownItem
: <InAppNotificationList inAppNotificationData={inAppNotificationData} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const InAppNotificationPage: FC = () => {
)}
{ notificationData != null && notificationData.docs.length === 0
// no items
? t('in_app_notification.mark_all_as_read')
? t('in_app_notification.no_unread_messages')
// render list-group
: (
<InAppNotificationList inAppNotificationData={notificationData} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,6 @@ const AiChatModalSubstance = (): JSX.Element => {

const isGenerating = generatingAnswerMessage != null;

useEffect(() => {
// do nothing when the modal is closed or threadId is already set
if (threadId != null) {
return;
}

const createThread = async() => {
// create thread
try {
const res = await apiv3Post('/openai/thread');
const thread = res.data.thread;

setThreadId(thread.id);
}
catch (err) {
logger.error(err.toString());
toastError(t('modal_aichat.failed_to_create_or_retrieve_thread'));
}
};

createThread();
}, [t, threadId]);

const submit = useCallback(async(data: FormData) => {
// do nothing when the assistant is generating an answer
if (isGenerating) {
Expand All @@ -107,12 +84,28 @@ const AiChatModalSubstance = (): JSX.Element => {
const newAnswerMessage = { id: (logLength + 1).toString(), content: '' };
setGeneratingAnswerMessage(newAnswerMessage);

// create thread
let currentThreadId = threadId;
if (threadId == null) {
try {
const res = await apiv3Post('/openai/thread');
const thread = res.data.thread;

setThreadId(thread.id);
currentThreadId = thread.id;
}
catch (err) {
logger.error(err.toString());
toastError(t('modal_aichat.failed_to_create_or_retrieve_thread'));
}
}

// post message
try {
const response = await fetch('/_api/v3/openai/message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userMessage: data.input, threadId, summaryMode: data.summaryMode }),
body: JSON.stringify({ userMessage: data.input, threadId: currentThreadId, summaryMode: data.summaryMode }),
});

if (!response.ok) {
Expand Down
7 changes: 3 additions & 4 deletions apps/app/src/features/openai/server/models/thread-relation.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { addDays } from 'date-fns';
import type mongoose from 'mongoose';
import { type Model, type Document, Schema } from 'mongoose';

import { getOrCreateModel } from '~/server/util/mongoose-utils';

const DAYS_UNTIL_EXPIRATION = 30;
const DAYS_UNTIL_EXPIRATION = 3;

const generateExpirationDate = (): Date => {
const currentDate = new Date();
const expirationDate = new Date(currentDate.setDate(currentDate.getDate() + DAYS_UNTIL_EXPIRATION));
return expirationDate;
return addDays(new Date(), DAYS_UNTIL_EXPIRATION);
};

interface ThreadRelation {
Expand Down
36 changes: 27 additions & 9 deletions apps/app/src/features/openai/server/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import { ErrorV3 } from '@growi/core/dist/models';
import express from 'express';

import { postMessageHandlersFactory } from './message';
import { rebuildVectorStoreHandlersFactory } from './rebuild-vector-store';
import { createThreadHandlersFactory } from './thread';
import type Crowi from '~/server/crowi';
import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';

import { isAiEnabled } from '../services';

const router = express.Router();

module.exports = (crowi) => {
router.post('/rebuild-vector-store', rebuildVectorStoreHandlersFactory(crowi));

// create thread
router.post('/thread', createThreadHandlersFactory(crowi));
// post message and return streaming with SSE
router.post('/message', postMessageHandlersFactory(crowi));
export const factory = (crowi: Crowi): express.Router => {

// disable all routes if AI is not enabled
if (!isAiEnabled()) {
router.all('*', (req, res: ApiV3Response) => {
return res.apiv3Err(new ErrorV3('GROWI AI is not enabled'), 501);
});
}
// enabled
else {
import('./rebuild-vector-store').then(({ rebuildVectorStoreHandlersFactory }) => {
router.post('/rebuild-vector-store', rebuildVectorStoreHandlersFactory(crowi));
});

import('./thread').then(({ createThreadHandlersFactory }) => {
router.post('/thread', createThreadHandlersFactory(crowi));
});

import('./message').then(({ postMessageHandlersFactory }) => {
router.post('/message', postMessageHandlersFactory(crowi));
});
}

return router;
};
2 changes: 1 addition & 1 deletion apps/app/src/features/openai/server/routes/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-respo
import loggerFactory from '~/utils/logger';

import { MessageErrorCode, type StreamErrorCode } from '../../interfaces/message-error';
import { openaiClient } from '../services';
import { openaiClient } from '../services/client';
import { getStreamErrorCode } from '../services/getStreamErrorCode';
import { replaceAnnotationWithPageLink } from '../services/replace-annotation-with-page-link';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ const AssistantType = {
CHAT: 'Chat',
} as const;

const AssistantDefaultModelMap: Record<AssistantType, OpenAI.Chat.ChatModel> = {
[AssistantType.SEARCH]: 'gpt-4o-mini',
[AssistantType.CHAT]: 'gpt-4o-mini',
};

const getAssistantModelByType = (type: AssistantType): OpenAI.Chat.ChatModel => {
const configKey = `openai:assistantModel:${type.toLowerCase()}`;
return configManager.getConfig('crowi', configKey) ?? AssistantDefaultModelMap[type];
};

type AssistantType = typeof AssistantType[keyof typeof AssistantType];


Expand All @@ -34,22 +44,23 @@ const findAssistantByName = async(assistantName: string): Promise<OpenAI.Beta.As
return findAssistant(storedAssistants);
};

const getOrCreateAssistant = async(type: AssistantType): Promise<OpenAI.Beta.Assistant> => {
const getOrCreateAssistant = async(type: AssistantType, nameSuffix?: string): Promise<OpenAI.Beta.Assistant> => {
const appSiteUrl = configManager.getConfig('crowi', 'app:siteUrl');
const assistantNameSuffix = configManager.getConfig('crowi', 'openai:assistantNameSuffix');
const assistantName = `GROWI ${type} Assistant for ${appSiteUrl}${assistantNameSuffix != null ? ` ${assistantNameSuffix}` : ''}`;
const assistantName = `GROWI ${type} Assistant for ${appSiteUrl}${nameSuffix != null ? ` ${nameSuffix}` : ''}`;
const assistantModel = getAssistantModelByType(type);

const assistant = await findAssistantByName(assistantName)
?? (
await openaiClient.beta.assistants.create({
name: assistantName,
model: 'gpt-4o',
model: assistantModel,
}));

// update instructions
const instructions = configManager.getConfig('crowi', 'openai:chatAssistantInstructions');
openaiClient.beta.assistants.update(assistant.id, {
instructions,
model: assistantModel,
tools: [{ type: 'file_search' }],
});

Expand Down
20 changes: 20 additions & 0 deletions apps/app/src/features/openai/server/services/cron/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import loggerFactory from '~/utils/logger';

import { isAiEnabled } from '../is-ai-enabled';


const logger = loggerFactory('growi:openai:service:cron');

export const startCronIfEnabled = async(): Promise<void> => {
if (isAiEnabled()) {
logger.info('Starting cron service for thread deletion');
const { ThreadDeletionCronService } = await import('./thread-deletion-cron');
const threadDeletionCronService = new ThreadDeletionCronService();
threadDeletionCronService.startCron();

logger.info('Starting cron service for vector store file deletion');
const { VectorStoreFileDeletionCronService } = await import('./vector-store-file-deletion-cron');
const vectorStoreFileDeletionCronService = new VectorStoreFileDeletionCronService();
vectorStoreFileDeletionCronService.startCron();
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { configManager } from '~/server/service/config-manager';
import loggerFactory from '~/utils/logger';
import { getRandomIntInRange } from '~/utils/rand';

import { getOpenaiService, type IOpenaiService } from './openai';
import { isAiEnabled } from '../is-ai-enabled';
import { getOpenaiService, type IOpenaiService } from '../openai';


const logger = loggerFactory('growi:service:thread-deletion-cron');

class ThreadDeletionCronService {
export class ThreadDeletionCronService {

cronJob: nodeCron.ScheduledTask;

Expand All @@ -25,8 +27,7 @@ class ThreadDeletionCronService {
sleep = (msec: number): Promise<void> => new Promise(resolve => setTimeout(resolve, msec));

startCron(): void {
const isAiEnabled = configManager.getConfig('crowi', 'app:aiEnabled');
if (!isAiEnabled) {
if (!isAiEnabled()) {
return;
}

Expand Down Expand Up @@ -67,5 +68,3 @@ class ThreadDeletionCronService {
}

}

export default ThreadDeletionCronService;
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { configManager } from '~/server/service/config-manager';
import loggerFactory from '~/utils/logger';
import { getRandomIntInRange } from '~/utils/rand';

import { getOpenaiService, type IOpenaiService } from './openai';
import { isAiEnabled } from '../is-ai-enabled';
import { getOpenaiService, type IOpenaiService } from '../openai';

const logger = loggerFactory('growi:service:vector-store-file-deletion-cron');

class VectorStoreFileDeletionCronService {
export class VectorStoreFileDeletionCronService {

cronJob: nodeCron.ScheduledTask;

Expand All @@ -25,8 +26,7 @@ class VectorStoreFileDeletionCronService {
sleep = (msec: number): Promise<void> => new Promise(resolve => setTimeout(resolve, msec));

startCron(): void {
const isAiEnabled = configManager.getConfig('crowi', 'app:aiEnabled');
if (!isAiEnabled) {
if (!isAiEnabled()) {
return;
}

Expand Down Expand Up @@ -67,5 +67,3 @@ class VectorStoreFileDeletionCronService {
}

}

export default VectorStoreFileDeletionCronService;
3 changes: 1 addition & 2 deletions apps/app/src/features/openai/server/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './embeddings';
export * from './client';
export * from './is-ai-enabled';
3 changes: 3 additions & 0 deletions apps/app/src/features/openai/server/services/is-ai-enabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { configManager } from '~/server/service/config-manager';

export const isAiEnabled = (): boolean => configManager.getConfig('crowi', 'app:aiEnabled');
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './normalize-thread-relation-expired-at';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './normalize-thread-relation-expired-at';
Loading
Loading