From 2b848d2f326ce9ba09d8471da0b82fa3a9fed575 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sat, 8 Nov 2025 00:50:31 +0300 Subject: [PATCH 01/13] init openai --- package.json | 4 ++- src/models/eventsFactory.js | 22 ++++++++++++++++ src/openai/index.ts | 27 +++++++++++++++++++ src/openai/inputs/eventSolving.ts | 15 +++++++++++ src/openai/instructions/cto.ts | 1 + src/openai/types.ts | 0 src/resolvers/event.js | 7 +++++ src/typeDefs/event.ts | 5 ++++ yarn.lock | 44 +++++++++++++++++++++++++++++++ 9 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/openai/index.ts create mode 100644 src/openai/inputs/eventSolving.ts create mode 100644 src/openai/instructions/cto.ts create mode 100644 src/openai/types.ts diff --git a/package.json b/package.json index 7c0362ac..303e1756 100644 --- a/package.json +++ b/package.json @@ -81,9 +81,11 @@ "mime-types": "^2.1.25", "mongodb": "^3.7.3", "morgan": "^1.10.1", + "node-fetch": "^3.3.2", + "openai": "^6.8.1", "prom-client": "^15.1.3", "safe-regex": "^2.1.0", "ts-node-dev": "^2.0.0", "uuid": "^8.3.2" } -} +} \ No newline at end of file diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index a5166602..93bb78b1 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -7,6 +7,7 @@ const mongo = require('../mongo'); const Event = require('../models/event'); const { ObjectID } = require('mongodb'); const { composeEventPayloadByRepetition } = require('../utils/merge'); +const { openAIApi } = require('../openai'); const MAX_DB_READ_BATCH_SIZE = Number(process.env.MAX_DB_READ_BATCH_SIZE); @@ -178,6 +179,27 @@ class EventsFactory extends Factory { return new Event(searchResult); } + /** + * Ask AI about solving repetition + * + * @param {string} repetitionId - repetition ID + * @param {string} eventId - event ID + * + * @return {string} - AI answer + */ + async askAi(repetitionId, eventId) { + const repetition = await this.getEventRepetition(repetitionId, eventId); + + if (!repetition) { + throw new Error('Repetition not found'); + } + + const payload = repetition.event.payload; + const solution = await openAIApi.solveEvent(payload); + + return solution; + } + /** * Returns events that grouped by day * diff --git a/src/openai/index.ts b/src/openai/index.ts new file mode 100644 index 00000000..677e2b22 --- /dev/null +++ b/src/openai/index.ts @@ -0,0 +1,27 @@ +import { EventAddons, EventData } from "@hawk.so/types"; +import OpenAI from "openai"; +import { eventSolvingInput } from "./inputs/eventSolving"; +import { ctoInstruction } from "./instructions/cto"; + +class OpenAIApi { + private readonly client: OpenAI; + + constructor() { + this.client = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, + // fetch, + }); + } + + async solveEvent(payload: EventData) { + const response = await this.client.responses.create({ + model: "gpt-4o", + instructions: ctoInstruction, + input: eventSolvingInput(payload), + }); + + return response.output; + } +} + +export const openAIApi = new OpenAIApi(); \ No newline at end of file diff --git a/src/openai/inputs/eventSolving.ts b/src/openai/inputs/eventSolving.ts new file mode 100644 index 00000000..5bc1e48e --- /dev/null +++ b/src/openai/inputs/eventSolving.ts @@ -0,0 +1,15 @@ +import { EventData, EventAddons } from "@hawk.so/types" + +export const eventSolvingInput = (payload: EventData) => ` + +Проанализируй ошибку и предложи решение + +Payload: ${JSON.stringify(payload)} + +Response: + +{ + "solution": "...", + "explanation": "..." +} +` \ No newline at end of file diff --git a/src/openai/instructions/cto.ts b/src/openai/instructions/cto.ts new file mode 100644 index 00000000..c785fc4e --- /dev/null +++ b/src/openai/instructions/cto.ts @@ -0,0 +1 @@ +export const ctoInstruction = "Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение" \ No newline at end of file diff --git a/src/openai/types.ts b/src/openai/types.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/resolvers/event.js b/src/resolvers/event.js index 5b8804da..8ab94184 100644 --- a/src/resolvers/event.js +++ b/src/resolvers/event.js @@ -89,6 +89,13 @@ module.exports = { return factory.findChartData(days, timezoneOffset, groupHash); }, + async askAi({ projectId, id: eventId, originalEventId }, _args, context) { + const factory = getEventsFactory(context, projectId); + const aiAnswer = await factory.askAi(eventId, originalEventId); + + return aiAnswer; + }, + /** * Return release data for the event * diff --git a/src/typeDefs/event.ts b/src/typeDefs/event.ts index ead9af4a..c7daafd6 100644 --- a/src/typeDefs/event.ts +++ b/src/typeDefs/event.ts @@ -262,6 +262,11 @@ type Event { """ repetitionsPortion(cursor: String = null, limit: Int = 10): RepetitionsPortion! + """ + AI solution for the event + """ + askAi: String! + """ Array of users who visited event """ diff --git a/yarn.lock b/yarn.lock index deea0104..6e8e16fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2343,6 +2343,11 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -3095,6 +3100,14 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -3227,6 +3240,13 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -5091,6 +5111,11 @@ node-addon-api@^5.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA== +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-fetch@^2.6.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" @@ -5105,6 +5130,15 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-fetch@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -5313,6 +5347,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +openai@^6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/openai/-/openai-6.8.1.tgz#72610890aa6f67b3473c7be2e2d6ff25ebb9846a" + integrity sha512-ACifslrVgf+maMz9vqwMP4+v9qvx5Yzssydizks8n+YUJ6YwUoxj51sKRQ8HYMfR6wgKLSIlaI108ZwCk+8yig== + optional-require@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/optional-require/-/optional-require-1.1.8.tgz#16364d76261b75d964c482b2406cb824d8ec44b7" @@ -6927,6 +6966,11 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.12" +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From 349094cf6a804e5be238973371b14947723acc75 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 01:58:37 +0300 Subject: [PATCH 02/13] feat(ai): added base for ai suggestions --- package.json | 12 ++-- src/models/eventsFactory.js | 20 +++--- src/openai/index.ts | 27 ------- src/openai/instructions/cto.ts | 1 - src/openai/types.ts | 0 src/resolvers/event.js | 13 +++- src/resolvers/vercel-ai/index.ts | 40 +++++++++++ .../vercel-ai}/inputs/eventSolving.ts | 4 +- src/resolvers/vercel-ai/instructions/cto.ts | 1 + src/typeDefs/event.ts | 4 +- src/types/vercel-ai.d.ts | 18 +++++ src/types/zod-v4.d.ts | 5 ++ yarn.lock | 70 ++++++++++++++++++- 13 files changed, 165 insertions(+), 50 deletions(-) delete mode 100644 src/openai/index.ts delete mode 100644 src/openai/instructions/cto.ts delete mode 100644 src/openai/types.ts create mode 100644 src/resolvers/vercel-ai/index.ts rename src/{openai => resolvers/vercel-ai}/inputs/eventSolving.ts (80%) create mode 100644 src/resolvers/vercel-ai/instructions/cto.ts create mode 100644 src/types/vercel-ai.d.ts create mode 100644 src/types/zod-v4.d.ts diff --git a/package.json b/package.json index 722fe26d..4681de9a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dev:up": "docker-compose -f docker-compose.dev.yml up -d", "dev:down": "docker-compose -f docker-compose.dev.yml down", "build": "tsc", - "convert": "node ./convertors/set-user-project-last-visit.js", + "convert": "node ./convertors/set-user-workspaces-membership.js", "migrations:create": "docker-compose exec api yarn migrate-mongo create", "migrations:up": "docker-compose exec api yarn migrate-mongo up", "migrations:down": "docker-compose exec api yarn migrate-mongo down", @@ -33,6 +33,7 @@ "typescript": "^4.7.4" }, "dependencies": { + "@ai-sdk/openai": "^2.0.64", "@amplitude/node": "^1.10.0", "@graphql-tools/merge": "^8.3.1", "@graphql-tools/schema": "^8.5.1", @@ -55,6 +56,7 @@ "@types/node-fetch": "^2.5.4", "@types/safe-regex": "^1.1.6", "@types/uuid": "^8.3.4", + "ai": "^5.0.89", "amqp-connection-manager": "^3.1.0", "amqplib": "^0.5.5", "apollo-server-express": "^3.10.0", @@ -86,6 +88,8 @@ "prom-client": "^15.1.3", "safe-regex": "^2.1.0", "ts-node-dev": "^2.0.0", - "uuid": "^8.3.2" - } -} \ No newline at end of file + "uuid": "^8.3.2", + "zod": "^4.1.12" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" +} diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index c481b43b..80ab3770 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -7,7 +7,7 @@ const mongo = require('../mongo'); const Event = require('../models/event'); const { ObjectID } = require('mongodb'); const { composeEventPayloadByRepetition } = require('../utils/merge'); -const { openAIApi } = require('../openai'); +const { vercelAIApi } = require('../resolvers/vercel-ai'); const MAX_DB_READ_BATCH_SIZE = Number(process.env.MAX_DB_READ_BATCH_SIZE); @@ -180,22 +180,22 @@ class EventsFactory extends Factory { } /** - * Ask AI about solving repetition + * Generate AI suggestion for the event * - * @param {string} repetitionId - repetition ID - * @param {string} eventId - event ID - * - * @return {string} - AI answer + * @param {string} projectId - event's project + * @param {string} eventId - event id + * @param {string} originalEventId - original event id + * @returns {Promise} AI suggestion for the event */ - async askAi(repetitionId, eventId) { - const repetition = await this.getEventRepetition(repetitionId, eventId); + async aiSuggestion(eventId, originalEventId) { + const repetition = await this.getEventRepetition(eventId, originalEventId); if (!repetition) { throw new Error('Repetition not found'); } - const payload = repetition.event.payload; - const solution = await openAIApi.solveEvent(payload); + const payload = repetition.payload; + const solution = await vercelAIApi.generateSuggestion(payload); return solution; } diff --git a/src/openai/index.ts b/src/openai/index.ts deleted file mode 100644 index 677e2b22..00000000 --- a/src/openai/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { EventAddons, EventData } from "@hawk.so/types"; -import OpenAI from "openai"; -import { eventSolvingInput } from "./inputs/eventSolving"; -import { ctoInstruction } from "./instructions/cto"; - -class OpenAIApi { - private readonly client: OpenAI; - - constructor() { - this.client = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY, - // fetch, - }); - } - - async solveEvent(payload: EventData) { - const response = await this.client.responses.create({ - model: "gpt-4o", - instructions: ctoInstruction, - input: eventSolvingInput(payload), - }); - - return response.output; - } -} - -export const openAIApi = new OpenAIApi(); \ No newline at end of file diff --git a/src/openai/instructions/cto.ts b/src/openai/instructions/cto.ts deleted file mode 100644 index c785fc4e..00000000 --- a/src/openai/instructions/cto.ts +++ /dev/null @@ -1 +0,0 @@ -export const ctoInstruction = "Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение" \ No newline at end of file diff --git a/src/openai/types.ts b/src/openai/types.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/resolvers/event.js b/src/resolvers/event.js index 8ab94184..257901b4 100644 --- a/src/resolvers/event.js +++ b/src/resolvers/event.js @@ -89,11 +89,18 @@ module.exports = { return factory.findChartData(days, timezoneOffset, groupHash); }, - async askAi({ projectId, id: eventId, originalEventId }, _args, context) { + /** + * Return AI suggestion for the event + * + * @param {string} projectId - event's project + * @param {string} eventId - event id + * @param {string} originalEventId - original event id + * @returns {Promise} AI suggestion for the event + */ + async aiSuggestion({ projectId, id: eventId, originalEventId }, _args, context) { const factory = getEventsFactory(context, projectId); - const aiAnswer = await factory.askAi(eventId, originalEventId); - return aiAnswer; + return factory.aiSuggestion(eventId, originalEventId); }, /** diff --git a/src/resolvers/vercel-ai/index.ts b/src/resolvers/vercel-ai/index.ts new file mode 100644 index 00000000..fe9419aa --- /dev/null +++ b/src/resolvers/vercel-ai/index.ts @@ -0,0 +1,40 @@ +import { EventAddons, EventData } from '@hawk.so/types'; +import { generateText } from 'ai'; +import { openai } from '@ai-sdk/openai'; +import { eventSolvingInput } from './inputs/eventSolving'; +import { ctoInstruction } from './instructions/cto'; + +/** + * Vercel AI API + */ +class VercelAIApi { + /** + * Model ID + */ + private readonly modelId: string; + + constructor() { + /** + * @todo make it dynamic, get from project settings + */ + this.modelId = 'gpt-4o'; + } + + /** + * Generate AI suggestion for the event + * + * @param {EventData} payload - event data + * @returns {Promise} AI suggestion for the event + */ + public async generateSuggestion(payload: EventData) { + const { text } = await generateText({ + model: openai(this.modelId), + system: ctoInstruction, + prompt: eventSolvingInput(payload), + }); + + return text; + } +} + +export const vercelAIApi = new VercelAIApi(); diff --git a/src/openai/inputs/eventSolving.ts b/src/resolvers/vercel-ai/inputs/eventSolving.ts similarity index 80% rename from src/openai/inputs/eventSolving.ts rename to src/resolvers/vercel-ai/inputs/eventSolving.ts index 5bc1e48e..ac0412a1 100644 --- a/src/openai/inputs/eventSolving.ts +++ b/src/resolvers/vercel-ai/inputs/eventSolving.ts @@ -1,4 +1,4 @@ -import { EventData, EventAddons } from "@hawk.so/types" +import { EventData, EventAddons } from '@hawk.so/types'; export const eventSolvingInput = (payload: EventData) => ` @@ -12,4 +12,4 @@ Response: "solution": "...", "explanation": "..." } -` \ No newline at end of file +`; \ No newline at end of file diff --git a/src/resolvers/vercel-ai/instructions/cto.ts b/src/resolvers/vercel-ai/instructions/cto.ts new file mode 100644 index 00000000..520b90a9 --- /dev/null +++ b/src/resolvers/vercel-ai/instructions/cto.ts @@ -0,0 +1 @@ +export const ctoInstruction = 'Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение'; \ No newline at end of file diff --git a/src/typeDefs/event.ts b/src/typeDefs/event.ts index c7daafd6..0cc084dd 100644 --- a/src/typeDefs/event.ts +++ b/src/typeDefs/event.ts @@ -263,9 +263,9 @@ type Event { repetitionsPortion(cursor: String = null, limit: Int = 10): RepetitionsPortion! """ - AI solution for the event + AI suggestion for the event """ - askAi: String! + aiSuggestion: String! """ Array of users who visited event diff --git a/src/types/vercel-ai.d.ts b/src/types/vercel-ai.d.ts new file mode 100644 index 00000000..33d02c19 --- /dev/null +++ b/src/types/vercel-ai.d.ts @@ -0,0 +1,18 @@ +declare module 'ai' { + /** + * Minimal type for generateText used in server-side integration. + */ + export function generateText(input: { + model: any; + system?: string; + prompt: string; + }): Promise<{ text: string }>; +} + +declare module '@ai-sdk/openai' { + /** + * Minimal types for OpenAI provider. + */ + export function createOpenAI(config?: { apiKey?: string }): (model: string) => any; + export const openai: (model: string) => any; +} diff --git a/src/types/zod-v4.d.ts b/src/types/zod-v4.d.ts new file mode 100644 index 00000000..c0fc3475 --- /dev/null +++ b/src/types/zod-v4.d.ts @@ -0,0 +1,5 @@ +declare module 'zod/v4' { + export * from 'zod'; + import z from 'zod'; + export default z; +} diff --git a/yarn.lock b/yarn.lock index 6e8e16fd..16e18da0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,39 @@ # yarn lockfile v1 +"@ai-sdk/gateway@2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@ai-sdk/gateway/-/gateway-2.0.7.tgz#e3b77ef01658b47a19956313fc2a36b9e5a951f3" + integrity sha512-/AI5AKi4vOK9SEb8Z1dfXkhsJ5NAfWsoJQc96B/mzn2KIrjw5occOjIwD06scuhV9xWlghCoXJT1sQD9QH/tyg== + dependencies: + "@ai-sdk/provider" "2.0.0" + "@ai-sdk/provider-utils" "3.0.16" + "@vercel/oidc" "3.0.3" + +"@ai-sdk/openai@^2.0.64": + version "2.0.64" + resolved "https://registry.yarnpkg.com/@ai-sdk/openai/-/openai-2.0.64.tgz#d8746bd341c277b440d2ed54179bfe1b43e7853c" + integrity sha512-+1mqxn42uB32DPZ6kurSyGAmL3MgCaDpkYU7zNDWI4NLy3Zg97RxTsI1jBCGIqkEVvRZKJlIMYtb89OvMnq3AQ== + dependencies: + "@ai-sdk/provider" "2.0.0" + "@ai-sdk/provider-utils" "3.0.16" + +"@ai-sdk/provider-utils@3.0.16": + version "3.0.16" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-3.0.16.tgz#17b7170bf51a7a690bf0186490ce29a8ce50a961" + integrity sha512-lsWQY9aDXHitw7C1QRYIbVGmgwyT98TF3MfM8alNIXKpdJdi+W782Rzd9f1RyOfgRmZ08gJ2EYNDhWNK7RqpEA== + dependencies: + "@ai-sdk/provider" "2.0.0" + "@standard-schema/spec" "^1.0.0" + eventsource-parser "^3.0.6" + +"@ai-sdk/provider@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-2.0.0.tgz#b853c739d523b33675bc74b6c506b2c690bc602b" + integrity sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA== + dependencies: + json-schema "^0.4.0" + "@amplitude/identify@^1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@amplitude/identify/-/identify-1.10.0.tgz#d62b8b6785c29350c368810475a6fc7b04985210" @@ -725,7 +758,7 @@ resolved "https://registry.yarnpkg.com/@n1ru4l/json-patch-plus/-/json-patch-plus-0.2.0.tgz#b8fa09fd980c3460dfdc109a7c4cc5590157aa6b" integrity sha512-pLkJy83/rVfDTyQgDSC8GeXAHEdXNHGNJrB1b7wAyGQu0iv7tpMXntKVSqj0+XKNVQbco40SZffNfVALzIt0SQ== -"@opentelemetry/api@^1.4.0": +"@opentelemetry/api@1.9.0", "@opentelemetry/api@^1.4.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== @@ -811,6 +844,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@standard-schema/spec@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -1305,6 +1343,11 @@ semver "^7.3.2" tsutils "^3.17.1" +"@vercel/oidc@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@vercel/oidc/-/oidc-3.0.3.tgz#82c2b6dd4d5c3b37dcb1189718cdeb9db402d052" + integrity sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg== + abab@^2.0.3, abab@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -1363,6 +1406,16 @@ agent-base@6: dependencies: debug "4" +ai@^5.0.89: + version "5.0.89" + resolved "https://registry.yarnpkg.com/ai/-/ai-5.0.89.tgz#8929fbc18f247aa9e4442836a12aa84191edf2a4" + integrity sha512-8Nq+ZojGacQrupoJEQLrTDzT5VtR3gyp5AaqFSV3tzsAXlYQ9Igb7QE3yeoEdzOk5IRfDwWL7mDCUD+oBg1hDA== + dependencies: + "@ai-sdk/gateway" "2.0.7" + "@ai-sdk/provider" "2.0.0" + "@ai-sdk/provider-utils" "3.0.16" + "@opentelemetry/api" "1.9.0" + ajv@^6.10.0, ajv@^6.10.2: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -2933,6 +2986,11 @@ events@1.1.1: resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== +eventsource-parser@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + exec-sh@^0.3.2: version "0.3.6" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" @@ -4546,6 +4604,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -7198,3 +7261,8 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +zod@^4.1.12: + version "4.1.12" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.12.tgz#64f1ea53d00eab91853195653b5af9eee68970f0" + integrity sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ== From 1b1d83dda0ea3c0a9200200465591f6d0198ee62 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 02:35:17 +0300 Subject: [PATCH 03/13] fixes --- src/models/eventsFactory.js | 2 +- src/resolvers/event.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index 80ab3770..f18c2521 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -645,7 +645,7 @@ class EventsFactory extends Factory { /** * If originalEventId equals repetitionId than user wants to get first repetition which is original event */ - if (repetitionId === originalEventId) { + if (repetitionId.toString() === originalEventId.toString()) { const originalEvent = await this.eventsDataLoader.load(originalEventId); /** diff --git a/src/resolvers/event.js b/src/resolvers/event.js index 257901b4..24ca8b0f 100644 --- a/src/resolvers/event.js +++ b/src/resolvers/event.js @@ -97,7 +97,7 @@ module.exports = { * @param {string} originalEventId - original event id * @returns {Promise} AI suggestion for the event */ - async aiSuggestion({ projectId, id: eventId, originalEventId }, _args, context) { + async aiSuggestion({ projectId, _id: eventId, originalEventId }, _args, context) { const factory = getEventsFactory(context, projectId); return factory.aiSuggestion(eventId, originalEventId); From f70a64f27f2239c2b9085779fe2690caea94b627 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 8 Nov 2025 23:38:23 +0000 Subject: [PATCH 04/13] Bump version up to 1.2.23 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb7fbbe9..949565e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.2.22", + "version": "1.2.23", "main": "index.ts", "license": "BUSL-1.1", "scripts": { From 3c3f335cc2dc8c84242f3081ab80a2beb388ea8f Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 02:42:12 +0300 Subject: [PATCH 05/13] rm redundant packages --- package.json | 8 ++------ yarn.lock | 52 ---------------------------------------------------- 2 files changed, 2 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index 949565e8..eac81c72 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dev:up": "docker-compose -f docker-compose.dev.yml up -d", "dev:down": "docker-compose -f docker-compose.dev.yml down", "build": "tsc", - "convert": "node ./convertors/set-user-workspaces-membership.js", + "convert": "node ./convertors/set-user-project-last-visit.js", "migrations:create": "docker-compose exec api yarn migrate-mongo create", "migrations:up": "docker-compose exec api yarn migrate-mongo up", "migrations:down": "docker-compose exec api yarn migrate-mongo down", @@ -53,7 +53,6 @@ "@types/mongodb": "^3.6.20", "@types/morgan": "^1.9.10", "@types/node": "^16.11.46", - "@types/node-fetch": "^2.5.4", "@types/safe-regex": "^1.1.6", "@types/uuid": "^8.3.4", "ai": "^5.0.89", @@ -83,13 +82,10 @@ "mime-types": "^2.1.25", "mongodb": "^3.7.3", "morgan": "^1.10.1", - "node-fetch": "^3.3.2", - "openai": "^6.8.1", "prom-client": "^15.1.3", "safe-regex": "^2.1.0", "ts-node-dev": "^2.0.0", "uuid": "^8.3.2", "zod": "^4.1.12" - }, - "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" + } } diff --git a/yarn.lock b/yarn.lock index 16e18da0..a08708e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1187,14 +1187,6 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node-fetch@^2.5.4": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" - integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - "@types/node@*": version "18.6.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.2.tgz#ffc5f0f099d27887c8d9067b54e55090fcd54126" @@ -2396,11 +2388,6 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -data-uri-to-buffer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" - integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== - data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -3158,14 +3145,6 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -3298,13 +3277,6 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -5174,11 +5146,6 @@ node-addon-api@^5.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA== -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - node-fetch@^2.6.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" @@ -5193,15 +5160,6 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" - integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -5410,11 +5368,6 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -openai@^6.8.1: - version "6.8.1" - resolved "https://registry.yarnpkg.com/openai/-/openai-6.8.1.tgz#72610890aa6f67b3473c7be2e2d6ff25ebb9846a" - integrity sha512-ACifslrVgf+maMz9vqwMP4+v9qvx5Yzssydizks8n+YUJ6YwUoxj51sKRQ8HYMfR6wgKLSIlaI108ZwCk+8yig== - optional-require@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/optional-require/-/optional-require-1.1.8.tgz#16364d76261b75d964c482b2406cb824d8ec44b7" @@ -7029,11 +6982,6 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.12" -web-streams-polyfill@^3.0.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" - integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From 46f2b74a0408774906c7aefd216eca351468664e Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 03:12:19 +0300 Subject: [PATCH 06/13] fix zod --- package.json | 2 +- tsconfig.json | 9 ++++++--- yarn.lock | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index eac81c72..14c3db16 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,6 @@ "safe-regex": "^2.1.0", "ts-node-dev": "^2.0.0", "uuid": "^8.3.2", - "zod": "^4.1.12" + "zod": "^3.25.76" } } diff --git a/tsconfig.json b/tsconfig.json index 54843532..2f666715 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -41,13 +41,16 @@ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + "paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "zod/v4": ["src/types/zod-v4.d.ts"] + }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "skipLibCheck": true // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ diff --git a/yarn.lock b/yarn.lock index a08708e4..b26d9101 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7210,7 +7210,7 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== -zod@^4.1.12: - version "4.1.12" - resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.12.tgz#64f1ea53d00eab91853195653b5af9eee68970f0" - integrity sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ== +zod@^3.25.76: + version "3.25.76" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== From baffb2b3c8790dbbbec7eada293000dbaf0f46d9 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 03:24:41 +0300 Subject: [PATCH 07/13] fixes --- src/models/eventsFactory.js | 2 +- src/{resolvers => }/vercel-ai/index.ts | 0 src/{resolvers => }/vercel-ai/inputs/eventSolving.ts | 2 +- src/{resolvers => }/vercel-ai/instructions/cto.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/{resolvers => }/vercel-ai/index.ts (100%) rename src/{resolvers => }/vercel-ai/inputs/eventSolving.ts (99%) rename src/{resolvers => }/vercel-ai/instructions/cto.ts (67%) diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index f18c2521..02302b66 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -7,7 +7,7 @@ const mongo = require('../mongo'); const Event = require('../models/event'); const { ObjectID } = require('mongodb'); const { composeEventPayloadByRepetition } = require('../utils/merge'); -const { vercelAIApi } = require('../resolvers/vercel-ai'); +const { vercelAIApi } = require('../vercel-ai'); const MAX_DB_READ_BATCH_SIZE = Number(process.env.MAX_DB_READ_BATCH_SIZE); diff --git a/src/resolvers/vercel-ai/index.ts b/src/vercel-ai/index.ts similarity index 100% rename from src/resolvers/vercel-ai/index.ts rename to src/vercel-ai/index.ts diff --git a/src/resolvers/vercel-ai/inputs/eventSolving.ts b/src/vercel-ai/inputs/eventSolving.ts similarity index 99% rename from src/resolvers/vercel-ai/inputs/eventSolving.ts rename to src/vercel-ai/inputs/eventSolving.ts index ac0412a1..539df620 100644 --- a/src/resolvers/vercel-ai/inputs/eventSolving.ts +++ b/src/vercel-ai/inputs/eventSolving.ts @@ -12,4 +12,4 @@ Response: "solution": "...", "explanation": "..." } -`; \ No newline at end of file +`; diff --git a/src/resolvers/vercel-ai/instructions/cto.ts b/src/vercel-ai/instructions/cto.ts similarity index 67% rename from src/resolvers/vercel-ai/instructions/cto.ts rename to src/vercel-ai/instructions/cto.ts index 520b90a9..042b34f7 100644 --- a/src/resolvers/vercel-ai/instructions/cto.ts +++ b/src/vercel-ai/instructions/cto.ts @@ -1 +1 @@ -export const ctoInstruction = 'Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение'; \ No newline at end of file +export const ctoInstruction = 'Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение'; From a63f88ad2be6a5c64ebd40e4a0ed43b7992e39d6 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 03:28:38 +0300 Subject: [PATCH 08/13] review changes --- src/typeDefs/event.ts | 2 +- src/vercel-ai/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/typeDefs/event.ts b/src/typeDefs/event.ts index 0cc084dd..986b381a 100644 --- a/src/typeDefs/event.ts +++ b/src/typeDefs/event.ts @@ -265,7 +265,7 @@ type Event { """ AI suggestion for the event """ - aiSuggestion: String! + aiSuggestion: String """ Array of users who visited event diff --git a/src/vercel-ai/index.ts b/src/vercel-ai/index.ts index fe9419aa..3ec3c84c 100644 --- a/src/vercel-ai/index.ts +++ b/src/vercel-ai/index.ts @@ -5,11 +5,11 @@ import { eventSolvingInput } from './inputs/eventSolving'; import { ctoInstruction } from './instructions/cto'; /** - * Vercel AI API + * Interface for interacting with Vercel AI Gateway */ class VercelAIApi { /** - * Model ID + * Model ID to use for generating suggestions */ private readonly modelId: string; From 018b70ad69fc8c84a7cf372c634743fc71c0c6f6 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 03:29:59 +0300 Subject: [PATCH 09/13] add todo --- src/vercel-ai/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vercel-ai/index.ts b/src/vercel-ai/index.ts index 3ec3c84c..48010f25 100644 --- a/src/vercel-ai/index.ts +++ b/src/vercel-ai/index.ts @@ -25,6 +25,7 @@ class VercelAIApi { * * @param {EventData} payload - event data * @returns {Promise} AI suggestion for the event + * @todo add defence against invalid prompt injection */ public async generateSuggestion(payload: EventData) { const { text } = await generateText({ From ee14c206a94a66534a6a7bc3e28a31261aa766e4 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 03:54:55 +0300 Subject: [PATCH 10/13] added ai service, some refactorng --- src/{ => integrations}/vercel-ai/index.ts | 0 .../vercel-ai/inputs/eventSolving.ts | 0 .../vercel-ai/instructions/cto.ts | 0 src/models/eventsFactory.js | 22 --------------- src/resolvers/event.js | 3 ++- src/services/ai.ts | 27 +++++++++++++++++++ src/services/types.ts | 20 ++++++++++++++ 7 files changed, 49 insertions(+), 23 deletions(-) rename src/{ => integrations}/vercel-ai/index.ts (100%) rename src/{ => integrations}/vercel-ai/inputs/eventSolving.ts (100%) rename src/{ => integrations}/vercel-ai/instructions/cto.ts (100%) create mode 100644 src/services/ai.ts create mode 100644 src/services/types.ts diff --git a/src/vercel-ai/index.ts b/src/integrations/vercel-ai/index.ts similarity index 100% rename from src/vercel-ai/index.ts rename to src/integrations/vercel-ai/index.ts diff --git a/src/vercel-ai/inputs/eventSolving.ts b/src/integrations/vercel-ai/inputs/eventSolving.ts similarity index 100% rename from src/vercel-ai/inputs/eventSolving.ts rename to src/integrations/vercel-ai/inputs/eventSolving.ts diff --git a/src/vercel-ai/instructions/cto.ts b/src/integrations/vercel-ai/instructions/cto.ts similarity index 100% rename from src/vercel-ai/instructions/cto.ts rename to src/integrations/vercel-ai/instructions/cto.ts diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index 02302b66..c2bb8a43 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -7,7 +7,6 @@ const mongo = require('../mongo'); const Event = require('../models/event'); const { ObjectID } = require('mongodb'); const { composeEventPayloadByRepetition } = require('../utils/merge'); -const { vercelAIApi } = require('../vercel-ai'); const MAX_DB_READ_BATCH_SIZE = Number(process.env.MAX_DB_READ_BATCH_SIZE); @@ -179,27 +178,6 @@ class EventsFactory extends Factory { return new Event(searchResult); } - /** - * Generate AI suggestion for the event - * - * @param {string} projectId - event's project - * @param {string} eventId - event id - * @param {string} originalEventId - original event id - * @returns {Promise} AI suggestion for the event - */ - async aiSuggestion(eventId, originalEventId) { - const repetition = await this.getEventRepetition(eventId, originalEventId); - - if (!repetition) { - throw new Error('Repetition not found'); - } - - const payload = repetition.payload; - const solution = await vercelAIApi.generateSuggestion(payload); - - return solution; - } - /** * Returns events that grouped by day * diff --git a/src/resolvers/event.js b/src/resolvers/event.js index 24ca8b0f..8767566d 100644 --- a/src/resolvers/event.js +++ b/src/resolvers/event.js @@ -1,5 +1,6 @@ const getEventsFactory = require('./helpers/eventsFactory').default; const sendPersonalNotification = require('../utils/personalNotifications').default; +const { aiService } = require('../services/ai'); /** * See all types and fields here {@see ../typeDefs/event.graphql} @@ -100,7 +101,7 @@ module.exports = { async aiSuggestion({ projectId, _id: eventId, originalEventId }, _args, context) { const factory = getEventsFactory(context, projectId); - return factory.aiSuggestion(eventId, originalEventId); + return aiService.generateSuggestion(factory, eventId, originalEventId); }, /** diff --git a/src/services/ai.ts b/src/services/ai.ts new file mode 100644 index 00000000..e366be28 --- /dev/null +++ b/src/services/ai.ts @@ -0,0 +1,27 @@ +import { vercelAIApi } from '../integrations/vercel-ai/'; +import { EventsFactoryInterface } from './types'; + +/** + * Service for interacting with AI + */ +export class AIService { + /** + * Generate suggestion for the event + * + * @param eventsFactory - events factory + * @param eventId - event id + * @param originalEventId - original event id + * @returns {Promise} - suggestion + */ + public async generateSuggestion(eventsFactory: EventsFactoryInterface, eventId: string, originalEventId: string): Promise { + const event = await eventsFactory.getEventRepetition(eventId, originalEventId); + + if (!event) { + throw new Error('Event not found'); + } + + return vercelAIApi.generateSuggestion(event.payload); + } +} + +export const aiService = new AIService(); \ No newline at end of file diff --git a/src/services/types.ts b/src/services/types.ts new file mode 100644 index 00000000..e1f9ab9c --- /dev/null +++ b/src/services/types.ts @@ -0,0 +1,20 @@ +import { EventAddons, EventData } from '@hawk.so/types'; + +type Event = { + _id: string; + payload: EventData; +}; + +/** + * Interface for events factory + */ +export interface EventsFactoryInterface { + /** + * Get event repetition + * + * @param repetitionId - repetition id + * @param originalEventId - original event id + * @returns {Promise>} - event repetition + */ + getEventRepetition(repetitionId: string, originalEventId: string): Promise; +} \ No newline at end of file From 0c245b25941e4f7020534ddec2ea0ddb61b5501d Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 04:03:45 +0300 Subject: [PATCH 11/13] improve docs --- src/services/types.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/types.ts b/src/services/types.ts index e1f9ab9c..1b14501f 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -1,12 +1,15 @@ import { EventAddons, EventData } from '@hawk.so/types'; +/** + * Event type which is returned by events factory + */ type Event = { _id: string; payload: EventData; }; /** - * Interface for events factory + * Interface for interacting with events factory */ export interface EventsFactoryInterface { /** From 57dae6e8d09625056c606dc44832194759fc9d85 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 04:19:57 +0300 Subject: [PATCH 12/13] improve prompts --- src/integrations/vercel-ai/inputs/eventSolving.ts | 12 ++++-------- src/integrations/vercel-ai/instructions/cto.ts | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/integrations/vercel-ai/inputs/eventSolving.ts b/src/integrations/vercel-ai/inputs/eventSolving.ts index 539df620..a4d662e5 100644 --- a/src/integrations/vercel-ai/inputs/eventSolving.ts +++ b/src/integrations/vercel-ai/inputs/eventSolving.ts @@ -1,15 +1,11 @@ import { EventData, EventAddons } from '@hawk.so/types'; export const eventSolvingInput = (payload: EventData) => ` - -Проанализируй ошибку и предложи решение - Payload: ${JSON.stringify(payload)} -Response: +Предоставь ответ в следующем формате: -{ - "solution": "...", - "explanation": "..." -} +1. Описание проблемы +2. Решение проблемы +3. Описание того, как можно предотвратить подобную ошибку в будущем `; diff --git a/src/integrations/vercel-ai/instructions/cto.ts b/src/integrations/vercel-ai/instructions/cto.ts index 042b34f7..36c96868 100644 --- a/src/integrations/vercel-ai/instructions/cto.ts +++ b/src/integrations/vercel-ai/instructions/cto.ts @@ -1 +1 @@ -export const ctoInstruction = 'Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение'; +export const ctoInstruction = 'Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение. Ответь на русском языке.'; From ac48e5c21776922fa03a508343d358f892e7ee99 Mon Sep 17 00:00:00 2001 From: slaveeks Date: Sun, 9 Nov 2025 04:21:46 +0300 Subject: [PATCH 13/13] improve prompts --- src/integrations/vercel-ai/inputs/eventSolving.ts | 6 ------ src/integrations/vercel-ai/instructions/cto.ts | 10 +++++++++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/integrations/vercel-ai/inputs/eventSolving.ts b/src/integrations/vercel-ai/inputs/eventSolving.ts index a4d662e5..0969b048 100644 --- a/src/integrations/vercel-ai/inputs/eventSolving.ts +++ b/src/integrations/vercel-ai/inputs/eventSolving.ts @@ -2,10 +2,4 @@ import { EventData, EventAddons } from '@hawk.so/types'; export const eventSolvingInput = (payload: EventData) => ` Payload: ${JSON.stringify(payload)} - -Предоставь ответ в следующем формате: - -1. Описание проблемы -2. Решение проблемы -3. Описание того, как можно предотвратить подобную ошибку в будущем `; diff --git a/src/integrations/vercel-ai/instructions/cto.ts b/src/integrations/vercel-ai/instructions/cto.ts index 36c96868..9d267044 100644 --- a/src/integrations/vercel-ai/instructions/cto.ts +++ b/src/integrations/vercel-ai/instructions/cto.ts @@ -1 +1,9 @@ -export const ctoInstruction = 'Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение. Ответь на русском языке.'; +export const ctoInstruction = `Ты технический директор ИТ компании, тебе нужно пояснить ошибку и предложить решение. + +Предоставь ответ в следующем формате: + +1. Описание проблемы +2. Решение проблемы +3. Описание того, как можно предотвратить подобную ошибку в будущем + +Ответь на русском языке.`;