thread.ts 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import type { IUserHasId } from '@growi/core/dist/interfaces';
  2. import { SCOPE } from '@growi/core/dist/interfaces';
  3. import { ErrorV3 } from '@growi/core/dist/models';
  4. import type { Request, RequestHandler } from 'express';
  5. import type { ValidationChain } from 'express-validator';
  6. import { body } from 'express-validator';
  7. import type Crowi from '~/server/crowi';
  8. import { accessTokenParser } from '~/server/middlewares/access-token-parser';
  9. import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
  10. import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
  11. import loggerFactory from '~/utils/logger';
  12. import { ThreadType } from '../../interfaces/thread-relation';
  13. import { getOpenaiService } from '../services/openai';
  14. import { certifyAiService } from './middlewares/certify-ai-service';
  15. const logger = loggerFactory('growi:routes:apiv3:openai:thread');
  16. type ReqBody = {
  17. type: ThreadType;
  18. aiAssistantId?: string;
  19. initialUserMessage?: string;
  20. };
  21. type CreateThreadReq = Request<undefined, ApiV3Response, ReqBody> & {
  22. user: IUserHasId;
  23. };
  24. type CreateThreadFactory = (crowi: Crowi) => RequestHandler[];
  25. export const createThreadHandlersFactory: CreateThreadFactory = (crowi) => {
  26. const loginRequiredStrictly = require('~/server/middlewares/login-required')(
  27. crowi,
  28. );
  29. const validator: ValidationChain[] = [
  30. body('type')
  31. .isIn(Object.values(ThreadType))
  32. .withMessage('type must be one of "editor" or "knowledge"'),
  33. body('aiAssistantId')
  34. .optional()
  35. .isMongoId()
  36. .withMessage('aiAssistantId must be string'),
  37. body('initialUserMessage')
  38. .optional()
  39. .isString()
  40. .withMessage('initialUserMessage must be string'),
  41. ];
  42. return [
  43. accessTokenParser([SCOPE.WRITE.FEATURES.AI_ASSISTANT], {
  44. acceptLegacy: true,
  45. }),
  46. loginRequiredStrictly,
  47. certifyAiService,
  48. validator,
  49. apiV3FormValidator,
  50. async (req: CreateThreadReq, res: ApiV3Response) => {
  51. const openaiService = getOpenaiService();
  52. if (openaiService == null) {
  53. return res.apiv3Err(new ErrorV3('GROWI AI is not enabled'), 501);
  54. }
  55. const { type, aiAssistantId, initialUserMessage } = req.body;
  56. // express-validator ensures aiAssistantId is a string
  57. try {
  58. const thread = await openaiService.createThread(
  59. req.user._id,
  60. type,
  61. aiAssistantId,
  62. initialUserMessage,
  63. );
  64. return res.apiv3(thread);
  65. } catch (err) {
  66. logger.error(err);
  67. return res.apiv3Err(err);
  68. }
  69. },
  70. ];
  71. };