From 35db1b8d4eff7005665cb7d320029a45887aee99 Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 24 May 2025 17:20:49 +0300 Subject: [PATCH 01/10] First draft of workflow --- .../src/lib/ai/agents/feedback/agent.ts | 39 ++ .../handlers/check-feedback-validity.ts | 52 ++ .../feedback/handlers/improve-phrasing.ts | 66 +++ .../handlers/improve-spelling-and-grammar.ts | 43 ++ .../src/lib/ai/agents/feedback/types.ts | 36 ++ .../src/lib/ai/agents/feedback/workflow.png | Bin 0 -> 18368 bytes apps/backend/src/lib/ai/llm.ts | 3 + apps/backend/src/main.ts | 2 + apps/backend/src/routers/ai-router.ts | 44 ++ package-lock.json | 532 +++++++++++++++++- package.json | 8 +- 11 files changed, 806 insertions(+), 19 deletions(-) create mode 100644 apps/backend/src/lib/ai/agents/feedback/agent.ts create mode 100644 apps/backend/src/lib/ai/agents/feedback/handlers/check-feedback-validity.ts create mode 100644 apps/backend/src/lib/ai/agents/feedback/handlers/improve-phrasing.ts create mode 100644 apps/backend/src/lib/ai/agents/feedback/handlers/improve-spelling-and-grammar.ts create mode 100644 apps/backend/src/lib/ai/agents/feedback/types.ts create mode 100644 apps/backend/src/lib/ai/agents/feedback/workflow.png create mode 100644 apps/backend/src/lib/ai/llm.ts create mode 100644 apps/backend/src/routers/ai-router.ts 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..e53c79d7a --- /dev/null +++ b/apps/backend/src/lib/ai/agents/feedback/agent.ts @@ -0,0 +1,39 @@ +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: '' + }); + + 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..cbe796d05 --- /dev/null +++ b/apps/backend/src/lib/ai/agents/feedback/handlers/check-feedback-validity.ts @@ -0,0 +1,52 @@ +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. "Great Job" section that contains primarily negative critiques (misplaced feedback) + 3. "Think About" section that contains primarily praise (misplaced feedback) + 4. 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 0000000000000000000000000000000000000000..b1a5e111b8ee168440202a0b89a461d65b7ec96b GIT binary patch literal 18368 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 literal 0 HcmV?d00001 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 02925ac0e..1a925624f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,9 @@ "@emotion/server": "11.11.0", "@emotion/styled": "^11.14.0", "@json2csv/plainjs": "^7.0.6", + "@langchain/core": "^0.3.57", + "@langchain/langgraph": "^0.2.73", + "@langchain/openai": "^0.5.11", "@mui/icons-material": "^7.0.2", "@mui/material": "^7.0.2", "@mui/x-data-grid": "^8.1.0", @@ -55,7 +58,8 @@ "swr": "^2.3.3", "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", @@ -2896,6 +2900,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", @@ -4733,6 +4743,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", @@ -9892,7 +10095,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": { @@ -9944,6 +10146,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.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", @@ -10157,6 +10369,12 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "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/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -10640,6 +10858,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", @@ -10718,6 +10948,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", @@ -11528,7 +11770,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -12005,7 +12246,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" @@ -12061,7 +12301,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -12529,6 +12768,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", @@ -13288,6 +13536,15 @@ } } }, + "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-light": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", @@ -13870,7 +14127,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" @@ -15028,6 +15285,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", @@ -16079,6 +16345,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", @@ -16637,7 +16922,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -17077,6 +17361,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", @@ -18671,6 +18964,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", @@ -19072,6 +19374,73 @@ "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/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", @@ -20529,6 +20898,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.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -20714,11 +21092,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" @@ -20739,21 +21136,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", @@ -23668,6 +24062,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", @@ -23762,6 +24201,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", @@ -23807,6 +24255,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", @@ -23825,6 +24289,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", @@ -25835,7 +26311,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" @@ -27073,6 +27548,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", @@ -28004,7 +28485,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -29276,6 +29756,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", @@ -30115,7 +30604,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" @@ -30258,14 +30747,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 ac1baebcb..26842de6b 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,9 @@ "@emotion/server": "11.11.0", "@emotion/styled": "^11.14.0", "@json2csv/plainjs": "^7.0.6", + "@langchain/core": "^0.3.57", + "@langchain/langgraph": "^0.2.73", + "@langchain/openai": "^0.5.11", "@mui/icons-material": "^7.0.2", "@mui/material": "^7.0.2", "@mui/x-data-grid": "^8.1.0", @@ -58,7 +61,8 @@ "swr": "^2.3.3", "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", @@ -116,4 +120,4 @@ "express": "^5.1.0" } } -} \ No newline at end of file +} From d42183c5d538538cbcfd7790730e25f3020a19ce Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 24 May 2025 17:21:36 +0300 Subject: [PATCH 02/10] env template --- apps/backend/.env.local | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/backend/.env.local b/apps/backend/.env.local index af8292a90..5adcba6d5 100644 --- a/apps/backend/.env.local +++ b/apps/backend/.env.local @@ -7,6 +7,13 @@ LEMS_DOMAIN="http://localhost:4200" JWT_SECRET="m#g4kaj8^3i@agS~Vggjz" DASHBOARD_JWT_SECRET="d%^bey5QVq3t&#fs Date: Sat, 24 May 2025 17:24:41 +0300 Subject: [PATCH 03/10] Remove env files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 245da3c63..31b9a5c6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. +# .env files +**/*.env.local + # compiled output dist tmp From c1f34931c69cf8e966bbfc0ddb6380a5b4863397 Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 24 May 2025 17:24:56 +0300 Subject: [PATCH 04/10] Remove env files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 245da3c63..2c4dab282 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. +# .env files +**/.env.local + # compiled output dist tmp From e58fa8fc20e4512aff0ded6510d6e246bac613d8 Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 24 May 2025 17:39:42 +0300 Subject: [PATCH 05/10] Add run name --- .../backend/src/lib/ai/agents/feedback/agent.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/backend/src/lib/ai/agents/feedback/agent.ts b/apps/backend/src/lib/ai/agents/feedback/agent.ts index e53c79d7a..d4a9fe586 100644 --- a/apps/backend/src/lib/ai/agents/feedback/agent.ts +++ b/apps/backend/src/lib/ai/agents/feedback/agent.ts @@ -25,12 +25,17 @@ 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: '' - }); + const result = await workflow.invoke( + { + rubric, + feedback: rubric.feedback || { greatJob: '', thinkAbout: '' }, + isValid: true, + error: '' + }, + { + runName: 'enhance-feedback' + } + ); return result; } catch (error) { From b01c2b84d3c9280933ad37ae7d6915230c7cbea6 Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 24 May 2025 17:56:37 +0300 Subject: [PATCH 06/10] undo gitignore changes --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index 65c6ae2fe..245da3c63 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. -# .env files -**/.env.local -**/*.env.local - # compiled output dist tmp From c016bee73fbf368add6eb4211cbbbd71477df020 Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 24 May 2025 19:29:27 +0300 Subject: [PATCH 07/10] Fixed validity --- .../ai/agents/feedback/handlers/check-feedback-validity.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 index cbe796d05..6d14b11e6 100644 --- 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 @@ -7,9 +7,7 @@ export const checkFeedbackValidity = async (state: typeof GraphAnnotation.State) 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. "Great Job" section that contains primarily negative critiques (misplaced feedback) - 3. "Think About" section that contains primarily praise (misplaced feedback) - 4. Content that is unclear, incoherent, or cannot be meaningfully improved + 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)'} @@ -46,7 +44,7 @@ export const checkFeedbackValidity = async (state: typeof GraphAnnotation.State) } return { - isValid: result.isProblematic, + isValid: !result.isProblematic, error: result.reason || '' }; }; From 893d707de40a075011476150b5f90bf184711cda Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 31 May 2025 11:54:12 +0300 Subject: [PATCH 08/10] try to remove env local files from git ignore again (will template them later) --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ae987a6b6..375633ea8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. #.env files -**/.env.local +apps/backend/.env.local +apps/frontend/.env.local # compiled output dist From c7342259feafdf6ed9610a96e3247f5c8b44d3ab Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 12 Jul 2025 16:51:45 +0300 Subject: [PATCH 09/10] Fix conflicts --- apps/backend/.template.env | 9 ++++++++- package-lock.json | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) 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/package-lock.json b/package-lock.json index bf3f9b2a8..369496086 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10119,6 +10119,18 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -13274,6 +13286,21 @@ } } }, + "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.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": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", @@ -19178,6 +19205,12 @@ "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", From 0e81769e2edab5c7e315370b54053926f72634cb Mon Sep 17 00:00:00 2001 From: johnmeshulam <55348702+johnmeshulam@users.noreply.github.com> Date: Sat, 12 Jul 2025 16:52:42 +0300 Subject: [PATCH 10/10] Revert change to env files --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0a71b14e7..c537bd94b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. #.env files -apps/backend/.env.local -apps/frontend/.env.local +**/.env.local # compiled output dist