From 4109dbec8d3920d3fcc6e2aaf69bc2281fdf11e4 Mon Sep 17 00:00:00 2001 From: Suman991 Date: Fri, 10 Apr 2026 23:12:41 +0530 Subject: [PATCH] find interface by llm --- .gitignore | 2 + package-lock.json | 202 +++++++++++++++++++++++++++-- package.json | 2 + src/common/common.module.ts | 9 ++ src/common/services/llm.service.ts | 89 +++++++++++++ src/form/dto/query-form.dto.ts | 2 +- src/form/form.controller.ts | 5 + src/form/form.module.ts | 3 +- src/form/form.service.ts | 26 +++- src/form/types/field.type.ts | 3 +- 10 files changed, 325 insertions(+), 18 deletions(-) create mode 100644 src/common/common.module.ts create mode 100644 src/common/services/llm.service.ts diff --git a/.gitignore b/.gitignore index 4b56acf..bac2e21 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ /node_modules /build +test.js + # Logs logs *.log diff --git a/package-lock.json b/package-lock.json index 65bf576..fd4f4c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@cerebras/cerebras_cloud_sdk": "^1.64.1", "@nestjs/common": "^11.0.1", "@nestjs/config": "^4.0.3", "@nestjs/core": "^11.0.1", @@ -17,6 +18,7 @@ "@nestjs/swagger": "^11.2.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.4", + "dotenv": "^17.4.1", "mongoose": "^9.4.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", @@ -718,6 +720,36 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/@cerebras/cerebras_cloud_sdk": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/@cerebras/cerebras_cloud_sdk/-/cerebras_cloud_sdk-1.64.1.tgz", + "integrity": "sha512-eQ0udGHS9xrWANi56yCS/FMcbwtysugD73YWipp89+zarbm2pd5hxqrmGlFqafS4Pwyo7cU7Qv31am5jdjqXFg==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@cerebras/cerebras_cloud_sdk/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@cerebras/cerebras_cloud_sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2167,6 +2199,18 @@ "rxjs": "^7.1.0" } }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/@nestjs/config/node_modules/lodash": { "version": "4.17.23", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", @@ -2710,12 +2754,21 @@ "version": "24.12.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, "node_modules/@types/qs": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", @@ -3553,6 +3606,18 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -3615,6 +3680,18 @@ "node": ">=0.4.0" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", @@ -3810,7 +3887,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, "license": "MIT" }, "node_modules/babel-jest": { @@ -4437,7 +4513,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -4697,7 +4772,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -4744,9 +4818,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -4910,7 +4984,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5175,6 +5248,15 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -5518,7 +5600,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -5531,11 +5612,16 @@ "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, "node_modules/form-data/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -5545,7 +5631,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -5554,6 +5639,19 @@ "node": ">= 0.6" } }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, "node_modules/formidable": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", @@ -5890,7 +5988,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -5951,6 +6048,15 @@ "node": ">=10.17.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -7705,6 +7811,26 @@ "dev": true, "license": "MIT" }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -7715,6 +7841,48 @@ "lodash": "^4.17.21" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -9567,7 +9735,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { @@ -9758,6 +9925,15 @@ "defaults": "^1.0.3" } }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index bd37c4a..848bda2 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@cerebras/cerebras_cloud_sdk": "^1.64.1", "@nestjs/common": "^11.0.1", "@nestjs/config": "^4.0.3", "@nestjs/core": "^11.0.1", @@ -28,6 +29,7 @@ "@nestjs/swagger": "^11.2.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.4", + "dotenv": "^17.4.1", "mongoose": "^9.4.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", diff --git a/src/common/common.module.ts b/src/common/common.module.ts new file mode 100644 index 0000000..57318d4 --- /dev/null +++ b/src/common/common.module.ts @@ -0,0 +1,9 @@ +import { Module } from "@nestjs/common"; +import { LlmService } from "./services/llm.service"; + +@Module({ + imports:[], + controllers:[], + providers:[], +}) +export class CommonModule{} \ No newline at end of file diff --git a/src/common/services/llm.service.ts b/src/common/services/llm.service.ts new file mode 100644 index 0000000..a8bc52a --- /dev/null +++ b/src/common/services/llm.service.ts @@ -0,0 +1,89 @@ +import Cerebras from "@cerebras/cerebras_cloud_sdk"; +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { Form } from "src/form/schemas/form.schema"; + +const interfaceSchema = { + type: "object", + properties: { + fields: { + type: "array", + items: { + type: "object", + properties: { + keyName: { type: "string" }, + tsType: { type: "string" }, + includeInInterface: { type: "boolean" } + }, + required: ["keyName", "tsType", "includeInInterface"], + additionalProperties: false + } + } + }, + required: ["fields"], + additionalProperties: false +}; + +@Injectable() +export class LlmService { + private cerebras: Cerebras; + + constructor(private configService: ConfigService) { + this.cerebras = new Cerebras({ + apiKey: this.configService.get('CEREBRAS_API_KEY'), + }); + } + + async generateFormInterface(form: Form): Promise { + const relevant = form.fields! + .filter(f => f.keyType !== "button") + .map(({ keyName, keyType, keySubType, options }) => ({ + keyName, + keyType, + ...(keySubType && { keySubType }), + ...(keyType === "checkbox" && { hasOptions: !!options?.length }), + })); + + const completion = await this.cerebras.chat.completions.create({ + model: "llama3.1-8b", + messages: [ + { + role: "system", + content: `You are a TypeScript expert. Given form field descriptors, return the correct TypeScript type for each. +Type rules: + input[number|range] → number + input[file] → File | null + input[date|datetime-local|month|week|time] → string + input[text|email|password|tel|url|search|color|hidden] → string + textarea | select | radio → string + multiselect → string[] + checkbox with hasOptions: true → string[] + checkbox with hasOptions: false → boolean`, + }, + { + role: "user", + content: JSON.stringify(relevant), + }, + ], + response_format: { + type: "json_schema", + json_schema: { + name: "interface_schema", + strict: true, + schema: interfaceSchema, + }, + }, + }); + + const { fields: typed } = JSON.parse((completion.choices as any)[0].message.content); + + const tsInterface = `export interface ${form.name.replace(/\s+/g, '')}FormValues {\n${ + typed + .filter(f => f.includeInInterface) + .map(f => ` ${f.keyName}: ${f.tsType};`) + .join("\n") + }\n}`; + + return tsInterface; + } +} \ No newline at end of file diff --git a/src/form/dto/query-form.dto.ts b/src/form/dto/query-form.dto.ts index 7b3deda..a8efc37 100644 --- a/src/form/dto/query-form.dto.ts +++ b/src/form/dto/query-form.dto.ts @@ -49,7 +49,7 @@ export class QueryFormDto { example: false, description: 'true = deleted only, false = active only, omit = all', }) - // @Type(() => Boolean) + // @Type(() => Boolean) // don't use this(unreliable) @Transform(({ value }) => { if (value === 'true' || value === true) return true; if (value === 'false' || value === false) return false; diff --git a/src/form/form.controller.ts b/src/form/form.controller.ts index 073a2ff..d602bbc 100644 --- a/src/form/form.controller.ts +++ b/src/form/form.controller.ts @@ -35,6 +35,11 @@ export class FormController { async find(@Param('formId') formId: string) { return await this.formService.find(formId); } + @Get(':formId/interface') + @ApiOperation({summary:'Get interface for the form provided by llm'}) + async getInterface(@Param('formId') formId:string){ + return await this.formService.findInterface(formId); + } @Patch(':id/fields/:fieldId') @ApiOperation({ summary: 'update a field in a form' }) diff --git a/src/form/form.module.ts b/src/form/form.module.ts index dddbfec..b7cf04c 100644 --- a/src/form/form.module.ts +++ b/src/form/form.module.ts @@ -4,6 +4,7 @@ import { FormController } from './form.controller'; import { MongooseModule } from '@nestjs/mongoose'; import { Form, FormSchema } from './schemas/form.schema'; import { Field, FieldSchema } from './schemas/field.schema'; +import { LlmService } from 'src/common/services/llm.service'; @Module({ imports:[ @@ -13,6 +14,6 @@ import { Field, FieldSchema } from './schemas/field.schema'; ]) ], controllers: [FormController], - providers: [FormService], + providers: [FormService,LlmService], }) export class FormModule {} diff --git a/src/form/form.service.ts b/src/form/form.service.ts index d9428d4..f1f1235 100644 --- a/src/form/form.service.ts +++ b/src/form/form.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException, ServiceUnavailableException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Form, FormDocument, Status } from './schemas/form.schema'; import { Model } from 'mongoose'; @@ -8,6 +8,7 @@ import { CreateUpdateDto } from './dto/create-update.dto'; import { QueryFormDto } from './dto/query-form.dto'; import { FormQueryBuilder } from './helpers/form-query.builder'; import { PaginatedResponse } from 'src/interfaces/paginated-response.interface'; +import { LlmService } from 'src/common/services/llm.service'; // Reusable projections const LIST_PROJECTION = { @@ -22,7 +23,10 @@ const DETAIL_PROJECTION = { _id: 0, __v: 0 }; @Injectable() export class FormService { - constructor(@InjectModel(Form.name) private formModel: Model) {} + constructor( + @InjectModel(Form.name) private formModel: Model, + private llmService: LlmService, + ) {} async createOrUpdate(dto: CreateUpdateDto): Promise
{ if (!dto.id) { @@ -87,9 +91,27 @@ export class FormService { .select(DETAIL_PROJECTION) .exec(); if (!form) throw new NotFoundException(`Form ${formId} not found`); + return form; } + async findInterface(formId: string): Promise { + const form = await this.formModel + .findOne({ id: formId, deletedAt: null }) + .select(DETAIL_PROJECTION) + .exec(); + + if (!form) throw new NotFoundException(`Form ${formId} not found`); + + try{ + let tsInference=await this.llmService.generateFormInterface(form) + return tsInference + }catch(err){ + throw new ServiceUnavailableException('llm down') + } + +} + async updateField( formId: string, fieldId: string, diff --git a/src/form/types/field.type.ts b/src/form/types/field.type.ts index 2df5c87..f50e03b 100644 --- a/src/form/types/field.type.ts +++ b/src/form/types/field.type.ts @@ -1,5 +1,5 @@ // ─── Parent types ─── -export type ParentKeyType = "input" | "textarea" | "select" | "multiselect" | "checkbox"; +export type ParentKeyType = "input" | "textarea" | "select" | "multiselect" | "checkbox"|"button"; // ─── Child types (only for "input") ─── export type InputSubType = "text" | "email" | "number" | "password" | "button" | "color" | "date" | "datetime-local" | "file" | "hidden" | "image" | "month" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "time" | "url" | "week" ; @@ -11,6 +11,7 @@ export const PARENT_TYPE_LABELS: Readonly> = { select: "Select", multiselect: "Multiselect", checkbox: "Checkbox", + button:"button", } as const; export const INPUT_SUB_TYPE_LABELS: Readonly> = {