diff --git a/apps/backend/.template.env b/apps/backend/.template.env index 208610ab1..817f69ddc 100644 --- a/apps/backend/.template.env +++ b/apps/backend/.template.env @@ -27,4 +27,11 @@ SCHEDULER_URL="http://localhost:8000" DIGITALOCEAN_ENDPOINT="" DIGITALOCEAN_SPACE="" DIGITALOCEAN_KEY="" -DIGITALOCEAN_SECRET="" \ No newline at end of file +DIGITALOCEAN_SECRET="" + +# AI provider +LANGSMITH_TRACING=true +LANGSMITH_ENDPOINT="https://api.smith.langchain.com" +LANGSMITH_API_KEY="" +LANGSMITH_PROJECT="lems" +OPENAI_API_KEY="" \ No newline at end of file diff --git a/apps/backend/src/lib/ai/agents/feedback/agent.ts b/apps/backend/src/lib/ai/agents/feedback/agent.ts new file mode 100644 index 000000000..d4a9fe586 --- /dev/null +++ b/apps/backend/src/lib/ai/agents/feedback/agent.ts @@ -0,0 +1,44 @@ +import { z } from 'zod'; +import { START, StateGraph, END } from '@langchain/langgraph'; +import { GraphAnnotation, RubricSchema } from './types'; +import { checkFeedbackValidity } from './handlers/check-feedback-validity'; +import { improveSpellingAndGrammar } from './handlers/improve-spelling-and-grammar'; +import { improvePhrasing } from './handlers/improve-phrasing'; + +const graph = new StateGraph(GraphAnnotation) + .addNode('checkFeedbackValidity', checkFeedbackValidity) + .addNode('improveSpellingAndGrammar', improveSpellingAndGrammar) + .addNode('improvePhrasing', improvePhrasing); + +const continueIfValid = (state: typeof GraphAnnotation.State) => { + return state.isValid ? 'improveSpellingAndGrammar' : END; +}; + +graph + .addEdge(START, 'checkFeedbackValidity') + .addConditionalEdges('checkFeedbackValidity', continueIfValid) + .addEdge('improveSpellingAndGrammar', 'improvePhrasing') + .addEdge('improvePhrasing', END); + +const workflow = graph.compile(); + +type AgentInput = z.infer; +export async function enhanceFeedback(rubric: AgentInput) { + try { + const result = await workflow.invoke( + { + rubric, + feedback: rubric.feedback || { greatJob: '', thinkAbout: '' }, + isValid: true, + error: '' + }, + { + runName: 'enhance-feedback' + } + ); + + return result; + } catch (error) { + console.error('Error enhancing feedback:', error); + } +} diff --git a/apps/backend/src/lib/ai/agents/feedback/handlers/check-feedback-validity.ts b/apps/backend/src/lib/ai/agents/feedback/handlers/check-feedback-validity.ts new file mode 100644 index 000000000..6d14b11e6 --- /dev/null +++ b/apps/backend/src/lib/ai/agents/feedback/handlers/check-feedback-validity.ts @@ -0,0 +1,50 @@ +import { SystemMessage, HumanMessage } from '@langchain/core/messages'; +import { GraphAnnotation } from '../types'; +import { llm } from '../../../llm'; + +export const checkFeedbackValidity = async (state: typeof GraphAnnotation.State) => { + const analysisPrompt = ` + Analyze the following feedback for a FIRST LEGO League team rubric and determine if it's problematic. + Problematic feedback is defined as: + 1. Empty or extremely short feedback that provides no real insight (fewer than 5 meaningful words) + 2. Content that is unclear, incoherent, or cannot be meaningfully improved + + Great Job Feedback: ${state.feedback.greatJob || '(empty)'} + Think About Feedback: ${state.feedback.thinkAbout || '(empty)'} + + First analyze the sentiment and content of each feedback section. + Then determine if either section is problematic based on the criteria above. + Return a JSON object with { "isProblematic": boolean, "reason": string if problematic } + `; + + const response = await llm.invoke([ + new SystemMessage( + 'You are a feedback quality analysis assistant. Analyze feedback critically and objectively.' + ), + new HumanMessage(analysisPrompt), + new SystemMessage( + "You MUST return a JSON object with the keys 'isProblematic' and 'reason' if applicable. \ + Do not include any other text or explanations. The value of \"reason\" should be a string explaining the problem." + ) + ]); + + const content = response.content.toString(); + let result: { isProblematic: boolean; reason?: string }; + + try { + const jsonMatch = content.match(/{[\s\S]*}/); + if (jsonMatch) { + result = JSON.parse(jsonMatch[0]); + } else { + throw new Error('Invalid JSON format in LLM response'); + } + } catch (error) { + console.error('Error parsing feedback analysis:', error); + result = { isProblematic: true, reason: 'Invalid response format from LLM' }; + } + + return { + isValid: !result.isProblematic, + error: result.reason || '' + }; +}; diff --git a/apps/backend/src/lib/ai/agents/feedback/handlers/improve-phrasing.ts b/apps/backend/src/lib/ai/agents/feedback/handlers/improve-phrasing.ts new file mode 100644 index 000000000..31c18d6a0 --- /dev/null +++ b/apps/backend/src/lib/ai/agents/feedback/handlers/improve-phrasing.ts @@ -0,0 +1,66 @@ +import { SystemMessage, HumanMessage } from '@langchain/core/messages'; +import { GraphAnnotation } from '../types'; +import { rubricsSchemas } from '@lems/season'; +import { llm } from '../../../llm'; + +export const improvePhrasing = async (state: typeof GraphAnnotation.State) => { + const { rubric, feedback } = state; + + const systemPrompt = `You are an AI assistant designed to help improve feedback on team rubrics for a FIRST LEGO League competition. + Your goal is to enhance feedback clarity while preserving the original feedback's intent and emotion. + + The feedback consists of two parts: + 1. "greatJob" - Positive feedback highlighting what the team did well + 2. "thinkAbout" - Constructive feedback suggesting areas for improvement + + If the feedback is already good, return it as is without any changes. + If the feedback is in Hebrew (עברית), you must maintain it in Hebrew. Do NOT translate to any other language. + `; + + const context = ` + Rubric Category: ${rubric.category} + Rubric Scores: ${JSON.stringify(rubric.values, null, 2)} + + Localized rubric: ${JSON.stringify(rubricsSchemas[rubric.category], null, 2)}`; + + const greatJobPrompt = ` + Improve the phrasing of the following "Great Job" feedback to be clearer and more concise, while preserving the original meaning and emotion. + Only enhance phrasing that needs improvement; preserve good parts. + Use rubric terminology and avoid generic phrases. + + Context: + ${context} + + Original Feedback (with grammar and spelling fixed): + ${feedback.greatJob} + `; + + const thinkAboutPrompt = ` + Improve the phrasing of the following "Think About" feedback to be clearer, more constructive, and more concise, + while preserving the original meaning and emotion. Only enhance phrasing that needs improvement; preserve good parts. + Use rubric terminology and avoid generic phrases. + + Context: + ${context} + + Original Feedback (with grammar and spelling fixed): + ${feedback.thinkAbout} + `; + + const greatJobResponse = await llm.invoke([ + new SystemMessage(systemPrompt), + new HumanMessage(greatJobPrompt) + ]); + + const thinkAboutResponse = await llm.invoke([ + new SystemMessage(systemPrompt), + new HumanMessage(thinkAboutPrompt) + ]); + + return { + feedback: { + greatJob: greatJobResponse.content.toString(), + thinkAbout: thinkAboutResponse.content.toString() + } + }; +}; diff --git a/apps/backend/src/lib/ai/agents/feedback/handlers/improve-spelling-and-grammar.ts b/apps/backend/src/lib/ai/agents/feedback/handlers/improve-spelling-and-grammar.ts new file mode 100644 index 000000000..72d968a2c --- /dev/null +++ b/apps/backend/src/lib/ai/agents/feedback/handlers/improve-spelling-and-grammar.ts @@ -0,0 +1,43 @@ +import { SystemMessage, HumanMessage } from '@langchain/core/messages'; +import { GraphAnnotation } from '../types'; +import { llm } from '../../../llm'; + +export const improveSpellingAndGrammar = async (state: typeof GraphAnnotation.State) => { + const { feedback } = state; + + const greatJobPrompt = ` + Fix spelling and grammatical errors in the following text. Only fix errors, don't change the meaning. + IMPORTANT: If the text is in Hebrew (עברית), you must maintain it in Hebrew. Do NOT translate to any other language. + + ${feedback.greatJob} + `; + + const thinkAboutPrompt = ` + Fix spelling and grammatical errors in the following text. Only fix errors, don't change the meaning. + IMPORTANT: If the text is in Hebrew (עברית), you must maintain it in Hebrew. Do NOT translate to any other language. + + ${feedback.thinkAbout} + `; + + // Fix both feedback sections independently + const greatJobResponse = await llm.invoke([ + new SystemMessage( + 'You are a spelling and grammar correction assistant. You MUST preserve the original language of the text (Hebrew/עברית). DO NOT translate text into English or any other language.' + ), + new HumanMessage(greatJobPrompt) + ]); + + const thinkAboutResponse = await llm.invoke([ + new SystemMessage( + 'You are a spelling and grammar correction assistant. You MUST preserve the original language of the text (Hebrew/עברית). DO NOT translate text into English or any other language.' + ), + new HumanMessage(thinkAboutPrompt) + ]); + + return { + feedback: { + greatJob: greatJobResponse.content.toString(), + thinkAbout: thinkAboutResponse.content.toString() + } + }; +}; diff --git a/apps/backend/src/lib/ai/agents/feedback/types.ts b/apps/backend/src/lib/ai/agents/feedback/types.ts new file mode 100644 index 000000000..198eb2447 --- /dev/null +++ b/apps/backend/src/lib/ai/agents/feedback/types.ts @@ -0,0 +1,36 @@ +import { Annotation } from '@langchain/langgraph'; +import { z } from 'zod'; + +export const GraphAnnotation = Annotation.Root({ + rubric: Annotation>(), + feedback: Annotation>(), + + isValid: Annotation(), + error: Annotation() +}); + +const FeedbackSchema = z.object({ + greatJob: z.string().describe('Positive feedback highlighting what the team did well'), + thinkAbout: z.string().describe('Constructive feedback suggesting areas for improvement') +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const RubricSchema = z.object({ + category: z.string().describe('The judging category for this rubric'), + feedback: FeedbackSchema, + values: z + .record( + z.object({ + value: z.number().describe("A score between 1 and 4 indicating the team's performance"), + notes: z + .string() + .optional() + .describe( + 'Optional note explaining why the team received the highest score (4) for this value' + ) + }) + ) + .optional() + .describe('Rubric score values'), + awards: z.record(z.boolean()).optional().describe('Optional awards selections') +}); diff --git a/apps/backend/src/lib/ai/agents/feedback/workflow.png b/apps/backend/src/lib/ai/agents/feedback/workflow.png new file mode 100644 index 000000000..b1a5e111b Binary files /dev/null and b/apps/backend/src/lib/ai/agents/feedback/workflow.png differ diff --git a/apps/backend/src/lib/ai/llm.ts b/apps/backend/src/lib/ai/llm.ts new file mode 100644 index 000000000..379223678 --- /dev/null +++ b/apps/backend/src/lib/ai/llm.ts @@ -0,0 +1,3 @@ +import { ChatOpenAI } from '@langchain/openai'; + +export const llm = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 }); diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index a83242089..91cfae435 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -15,6 +15,7 @@ import dashboardRouter from './routers/dashboard/index'; import websocket from './websocket/index'; import wsAuth from './middlewares/websocket/auth'; import wsValidateDivision from './middlewares/websocket/division-validator'; +import aiRouter from './routers/ai-router'; const app = express(); const server = http.createServer(app); @@ -39,6 +40,7 @@ app.use('/auth', authRouter); app.use('/public', publicRouter); app.use('/dashboard', dashboardRouter); app.use('/api', apiRouter); +app.use('/ai', aiRouter); app.get('/status', (req, res) => { res.status(200).json({ ok: true }); diff --git a/apps/backend/src/routers/ai-router.ts b/apps/backend/src/routers/ai-router.ts new file mode 100644 index 000000000..20184f224 --- /dev/null +++ b/apps/backend/src/routers/ai-router.ts @@ -0,0 +1,44 @@ +import { Router } from 'express'; +import { enhanceFeedback } from '../lib/ai/agents/feedback/agent'; +import asyncHandler from 'express-async-handler'; +import { getRubric } from '@lems/database'; +import { ObjectId } from 'mongodb'; + +const router = Router(); + +router.post( + '/enhance-feedback', + asyncHandler(async (req, res) => { + const rubric = await getRubric({ _id: new ObjectId('679a55f199042139ba459aa0') }); + if (!rubric) { + res.status(404).json({ error: 'Rubric not found' }); + return; + } + console.log('Enhancing feedback for rubric:', rubric.teamId, rubric.category); + + try { + const { category, data } = rubric; + const { values, feedback, awards } = data; + const agentInput = { + category, + values, + feedback, + awards + }; + const result = await enhanceFeedback(agentInput); + res.status(200).json({ + message: 'Feedback enhancement process started successfully', + original: rubric.data?.feedback, + updated: result.feedback + }); + } catch (error) { + console.error('Error enhancing feedback:', error); + res.status(500).json({ + error: 'Failed to enhance feedback', + details: error instanceof Error ? error.message : 'Unknown error occurred' + }); + } + }) +); + +export default router; diff --git a/package-lock.json b/package-lock.json index ba2cb2815..f1daac30b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,9 @@ "@emotion/styled": "^11.14.1", "@hello-pangea/dnd": "^18.0.1", "@json2csv/plainjs": "^7.0.6", + "@langchain/core": "^0.3.57", + "@langchain/langgraph": "^0.2.73", + "@langchain/openai": "^0.5.11", "@mui/icons-material": "^7.1.2", "@mui/material": "^7.2.0", "@mui/material-nextjs": "^7.1.1", @@ -59,7 +62,8 @@ "swr": "^2.3.4", "timesync": "^1.0.11", "tslib": "^2.8.1", - "winston": "^3.17.0" + "winston": "^3.17.0", + "zod": "^3.25.28" }, "devDependencies": { "@babel/core": "^7.26.10", @@ -2905,6 +2909,12 @@ "dev": true, "license": "(Apache-2.0 AND BSD-3-Clause)" }, + "node_modules/@cfworker/json-schema": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", + "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "license": "MIT" + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -4830,6 +4840,199 @@ "tslib": "2" } }, + "node_modules/@langchain/core": { + "version": "0.3.57", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.57.tgz", + "integrity": "sha512-jz28qCTKJmi47b6jqhQ6vYRTG5jRpqhtPQjriRTB5wR8mgvzo6xKs0fG/kExS3ZvM79ytD1npBvgf8i19xOo9Q==", + "license": "MIT", + "dependencies": { + "@cfworker/json-schema": "^4.0.2", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": "^0.3.29", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^10.0.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/core/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@langchain/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@langchain/core/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@langchain/core/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/langgraph": { + "version": "0.2.73", + "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-0.2.73.tgz", + "integrity": "sha512-vw+IXV2Q7x/QaykNj3VE/Ak3aPlst3spkpM6zYtqwGkQlhLZU4Lb8PHHPjqNNYHSdOTDj9x4jIRUPZArGHx9Aw==", + "license": "MIT", + "dependencies": { + "@langchain/langgraph-checkpoint": "~0.0.17", + "@langchain/langgraph-sdk": "~0.0.32", + "uuid": "^10.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.36 <0.3.0 || >=0.3.40 < 0.4.0", + "zod-to-json-schema": "^3.x" + }, + "peerDependenciesMeta": { + "zod-to-json-schema": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-checkpoint": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.17.tgz", + "integrity": "sha512-6b3CuVVYx+7x0uWLG+7YXz9j2iBa+tn2AXvkLxzEvaAsLE6Sij++8PPbS2BZzC+S/FPJdWsz6I5bsrqL0BYrCA==", + "license": "MIT", + "dependencies": { + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.31 <0.4.0" + } + }, + "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/langgraph-sdk": { + "version": "0.0.78", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.78.tgz", + "integrity": "sha512-skkUDmEhClWzlsr8jRaS1VpXVBISm5OFd0MUtS1jKRL5pn08K+IJRvHnlzgum9x7Dste9KXGcIGVoR7cNKJQrw==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.31 <0.4.0", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@langchain/core": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-sdk/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@langchain/langgraph-sdk/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@langchain/langgraph/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/openai": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.5.11.tgz", + "integrity": "sha512-DAp7x+NfjSqDvKVMle8yb85nzz+3ctP7zGJaeRS0vLmvkY9qf/jRkowsM0mcsIiEUKhG/AHzWqvxbhktb/jJ6Q==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^4.96.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.48 <0.4.0" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -9698,7 +9901,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/json5": { @@ -9750,6 +9952,16 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/node-forge": { "version": "1.3.13", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.13.tgz", @@ -10440,6 +10652,18 @@ "js-yaml": "bin/js-yaml.js" } }, + "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": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -10531,6 +10755,18 @@ "node": ">= 14" } }, + "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": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -11794,7 +12030,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -12323,6 +12558,15 @@ "node": ">=0.8" } }, + "node_modules/console-table-printer": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.13.0.tgz", + "integrity": "sha512-Wl1rFO1NLonYBBjrdF2SMCnfNrKr8PPooPSnQBRX3LTJsnyGjBzLcwffo8wSKuJ0kr/rgC2Ltxb3Bpb072VA9w==", + "license": "MIT", + "dependencies": { + "simple-wcswidth": "^1.0.1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -13082,10 +13326,19 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "license": "MIT" }, "node_modules/decimal.js-light": { @@ -13664,7 +13917,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "iconv-lite": "^0.6.2" @@ -14823,6 +15076,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/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -15849,6 +16111,25 @@ "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/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/formik": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", @@ -16850,6 +17131,15 @@ "node": ">= 14" } }, + "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/hyperdyperid": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", @@ -18477,6 +18767,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-tiktoken": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz", + "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -18887,6 +19186,79 @@ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "license": "MIT" }, + "node_modules/langsmith": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.29.tgz", + "integrity": "sha512-JPF2B339qpYy9FyuY4Yz1aWYtgPlFc/a+VTj3L/JcFLHCiMP7+Ig8I9jO+o1QwVa+JU3iugL1RS0wwc+Glw0zA==", + "license": "MIT", + "dependencies": { + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } + } + }, + "node_modules/langsmith/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/langsmith/node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, + "node_modules/langsmith/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/langsmith/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/langsmith/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -20330,6 +20702,15 @@ "object-assign": "^4.1.0" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -20550,11 +20931,30 @@ "license": "MIT", "optional": true }, + "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-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==", - "dev": true, "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -20575,21 +20975,18 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, "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==", - "dev": true, "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==", - "dev": true, "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -23510,6 +23907,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "4.103.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.103.0.tgz", + "integrity": "sha512-eWcz9kdurkGOFDtd5ySS5y251H2uBgq9+1a2lTBnjMMzlexJ40Am5t6Mu76SSE87VvitPa0dkIAp75F+dZVC0g==", + "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" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.103", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.103.tgz", + "integrity": "sha512-hHTHp+sEz6SxFsp+SA+Tqrua3AbmlAw+Y//aEwdHrdZkYVRWdvWD3y5uPZ0flYOkgskaFWqZ/YGFm3FaFQ0pRw==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/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/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -23603,6 +24045,15 @@ "@oxc-resolver/binding-win32-x64-msvc": "5.3.0" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -23648,6 +24099,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", @@ -23666,6 +24133,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -25623,7 +26102,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -26780,6 +27258,12 @@ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "license": "MIT" }, + "node_modules/simple-wcswidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", + "integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==", + "license": "MIT" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -29021,6 +29505,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", @@ -29862,7 +30355,7 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -30036,14 +30529,23 @@ } }, "node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "version": "3.25.28", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.28.tgz", + "integrity": "sha512-/nt/67WYKnr5by3YS7LroZJbtcCBurDKKPBPWWzaxvVCGuG/NOsiKkrjoOhI8mJ+SQUXEbUzeB3S+6XDUEEj7Q==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", + "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 960b6bfdb..82b34a372 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ "@emotion/styled": "^11.14.1", "@hello-pangea/dnd": "^18.0.1", "@json2csv/plainjs": "^7.0.6", + "@langchain/core": "^0.3.57", + "@langchain/langgraph": "^0.2.73", + "@langchain/openai": "^0.5.11", "@mui/icons-material": "^7.1.2", "@mui/material": "^7.2.0", "@mui/material-nextjs": "^7.1.1", @@ -62,7 +65,8 @@ "swr": "^2.3.4", "timesync": "^1.0.11", "tslib": "^2.8.1", - "winston": "^3.17.0" + "winston": "^3.17.0", + "zod": "^3.25.28" }, "devDependencies": { "@babel/core": "^7.26.10",