thread.ts 2.7 KB

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