feat: Implement intelligent question type analysis and guided learning
- Add question type classification (mathematical, factual_simple, exploratory) - Implement conditional UI: no text fields for math questions - Add math-specific step-by-step guidance without revealing answers - Enhanced AI prompts for subject-specific teaching approaches - Remove unnecessary action buttons (Research Ideas, Try Experiments, Discuss with Others) - Improve user experience with focused, educational interactions
This commit is contained in:
934
html/kidsai/package-lock.json
generated
Executable file
934
html/kidsai/package-lock.json
generated
Executable file
@@ -0,0 +1,934 @@
|
||||
{
|
||||
"name": "kidsai-explorer",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kidsai-explorer",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.0.0",
|
||||
"express": "^4.18.2",
|
||||
"node-fetch": "^2.7.0",
|
||||
"openai": "^5.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.13.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.0.tgz",
|
||||
"integrity": "sha512-A0BJ5lrpJVSfnMMXjmeO0xUnoxqsBHWCoqqTnGwGYVdnctqXXUEhJOO7LxmgxJon9tEZFGpe0xPRX0h2v3AANQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.3.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.3",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.19.0",
|
||||
"serve-static": "1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"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/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-5.8.2.tgz",
|
||||
"integrity": "sha512-8C+nzoHYgyYOXhHGN6r0fcb4SznuEn1R7YZMvlqDbnCuE0FM2mm3T1HiYW6WIcMS/F1Of2up/cSPjLPaWt0X9Q==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"openai": "bin/cli"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ws": {
|
||||
"optional": true
|
||||
},
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-list": "^1.0.0",
|
||||
"side-channel-map": "^1.0.1",
|
||||
"side-channel-weakmap": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-map": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-weakmap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-map": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"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/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"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/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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
html/kidsai/package.json
Executable file
26
html/kidsai/package.json
Executable file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "kidsai-explorer",
|
||||
"version": "1.0.0",
|
||||
"description": "A kid-friendly AI frontend that encourages critical thinking and self-discovery",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "node server.js"
|
||||
},
|
||||
"keywords": [
|
||||
"education",
|
||||
"kids",
|
||||
"ai",
|
||||
"learning",
|
||||
"critical-thinking"
|
||||
],
|
||||
"author": "KidsAI Explorer",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.0.0",
|
||||
"express": "^4.18.2",
|
||||
"node-fetch": "^2.7.0",
|
||||
"openai": "^5.8.2"
|
||||
}
|
||||
}
|
||||
740
html/kidsai/server.js
Normal file
740
html/kidsai/server.js
Normal file
@@ -0,0 +1,740 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const fetch = require('node-fetch');
|
||||
const OpenAI = require('openai');
|
||||
|
||||
// Load environment variables from .env file
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3002;
|
||||
|
||||
// OpenAI Configuration (Primary AI service)
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY
|
||||
});
|
||||
|
||||
// Hugging Face API configuration (Backup)
|
||||
const HF_MODELS = [
|
||||
'microsoft/DialoGPT-small',
|
||||
'google/flan-t5-small',
|
||||
'facebook/blenderbot-400M-distill',
|
||||
'microsoft/DialoGPT-medium'
|
||||
];
|
||||
|
||||
const HF_API_TOKEN = process.env.HUGGING_FACE_TOKEN;
|
||||
|
||||
// Educational prompts for AI to guide thinking instead of giving direct answers
|
||||
const EDUCATIONAL_PROMPTS = {
|
||||
en: {
|
||||
systemPrompt: "You are an educational assistant for children. Instead of giving direct answers, ask 2-3 guiding questions that help children think through the problem themselves. Be encouraging and use simple language. Focus on the thinking process, not the answer.",
|
||||
prefix: "That's a great question! Let me help you think through this step by step. Instead of telling you the answer, here are some questions to guide your thinking:"
|
||||
},
|
||||
de: {
|
||||
systemPrompt: "Du bist ein Lernassistent für Kinder. Anstatt direkte Antworten zu geben, stelle 2-3 Leitfragen, die Kindern helfen, das Problem selbst zu durchdenken. Sei ermutigend und verwende einfache Sprache. Konzentriere dich auf den Denkprozess, nicht auf die Antwort.",
|
||||
prefix: "Das ist eine tolle Frage! Lass mich dir helfen, Schritt für Schritt darüber nachzudenken. Anstatt dir die Antwort zu sagen, hier sind einige Fragen, die dein Denken leiten:"
|
||||
}
|
||||
};
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.static('.'));
|
||||
|
||||
// Serve the main page
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'index.html'));
|
||||
});
|
||||
|
||||
// API endpoint for AI-powered educational guidance
|
||||
app.post('/api/ask', async (req, res) => {
|
||||
const { question, language = 'en' } = req.body;
|
||||
|
||||
if (!question || question.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Question is required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Get AI-powered guidance
|
||||
const aiGuidance = await getAIGuidance(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
guidance: aiGuidance,
|
||||
question: question,
|
||||
language: language
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('AI API Error:', error);
|
||||
|
||||
// Fallback to rule-based guidance if AI fails
|
||||
const fallbackGuidance = getFallbackGuidance(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
guidance: fallbackGuidance,
|
||||
question: question,
|
||||
language: language,
|
||||
fallback: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// API endpoint for getting the actual answer after thinking process
|
||||
app.post('/api/reveal-answer', async (req, res) => {
|
||||
const { question, language = 'en' } = req.body;
|
||||
|
||||
if (!question || question.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Question is required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the actual answer from AI
|
||||
const answer = await getActualAnswer(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
answer: answer,
|
||||
question: question,
|
||||
language: language
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Answer API Error:', error);
|
||||
|
||||
// Fallback answer
|
||||
const fallbackAnswer = getFallbackAnswer(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
answer: fallbackAnswer,
|
||||
question: question,
|
||||
language: language,
|
||||
fallback: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// API endpoint for checking math answers
|
||||
app.post('/api/check-math-answer', async (req, res) => {
|
||||
const { question, userAnswer, language = 'en' } = req.body;
|
||||
|
||||
if (!question || !userAnswer) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Question and user answer are required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Extract the math expression from the question
|
||||
const correctAnswer = calculateMathAnswer(question);
|
||||
const userAnswerNum = parseFloat(userAnswer.trim());
|
||||
|
||||
const isCorrect = !isNaN(userAnswerNum) && Math.abs(userAnswerNum - correctAnswer) < 0.001;
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
correct: isCorrect,
|
||||
expectedAnswer: correctAnswer,
|
||||
userAnswer: userAnswerNum,
|
||||
hint: !isCorrect ? (language === 'de' ? 'Überprüfe deine Rechnung nochmal.' : 'Check your calculation again.') : null
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Math check error:', error);
|
||||
res.json({
|
||||
success: true,
|
||||
correct: true, // Fallback to correct if calculation fails
|
||||
expectedAnswer: null,
|
||||
userAnswer: userAnswer
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// API endpoint for getting additional math hints
|
||||
app.post('/api/get-more-hints', async (req, res) => {
|
||||
const { question, language = 'en', previousHints = [] } = req.body;
|
||||
|
||||
if (!question) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Question is required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Generate additional hints based on the math problem
|
||||
const additionalHints = await generateAdditionalMathHints(question, language, previousHints);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
hints: additionalHints,
|
||||
question: question,
|
||||
language: language
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Additional hints error:', error);
|
||||
|
||||
// Fallback hints if AI fails
|
||||
const fallbackHints = getFallbackMathHints(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
hints: fallbackHints,
|
||||
question: question,
|
||||
language: language,
|
||||
fallback: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Function to calculate the correct answer for simple math expressions
|
||||
function calculateMathAnswer(question) {
|
||||
const questionLower = question.toLowerCase().trim();
|
||||
|
||||
// Extract numbers and operation from common question formats
|
||||
let match;
|
||||
|
||||
// Pattern: "what is X+Y" or "was ist X+Y"
|
||||
match = questionLower.match(/(?:what\s+is\s+|was\s+ist\s+)?(\d+(?:\.\d+)?)\s*\+\s*(\d+(?:\.\d+)?)/);
|
||||
if (match) {
|
||||
return parseFloat(match[1]) + parseFloat(match[2]);
|
||||
}
|
||||
|
||||
// Pattern: "what is X-Y"
|
||||
match = questionLower.match(/(?:what\s+is\s+|was\s+ist\s+)?(\d+(?:\.\d+)?)\s*-\s*(\d+(?:\.\d+)?)/);
|
||||
if (match) {
|
||||
return parseFloat(match[1]) - parseFloat(match[2]);
|
||||
}
|
||||
|
||||
// Pattern: "what is X*Y" or "X times Y"
|
||||
match = questionLower.match(/(?:what\s+is\s+|was\s+ist\s+)?(\d+(?:\.\d+)?)\s*[\*x]\s*(\d+(?:\.\d+)?)/);
|
||||
if (match) {
|
||||
return parseFloat(match[1]) * parseFloat(match[2]);
|
||||
}
|
||||
|
||||
// Pattern: "what is X/Y" or "X divided by Y"
|
||||
match = questionLower.match(/(?:what\s+is\s+|was\s+ist\s+)?(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)/);
|
||||
if (match) {
|
||||
return parseFloat(match[1]) / parseFloat(match[2]);
|
||||
}
|
||||
|
||||
// If no pattern matches, throw error
|
||||
throw new Error('Could not parse math expression');
|
||||
}
|
||||
|
||||
// Function to analyze question type and determine interaction mode
|
||||
function analyzeQuestionType(question) {
|
||||
const questionLower = question.toLowerCase().trim();
|
||||
|
||||
// Mathematical questions - should guide to calculation
|
||||
const mathPatterns = [
|
||||
/what\s+is\s+\d+\s*[\+\-\*\/]\s*\d+/,
|
||||
/\d+\s*[\+\-\*\/]\s*\d+/,
|
||||
/\d+\s*plus\s*\d+/,
|
||||
/\d+\s*minus\s*\d+/,
|
||||
/\d+\s*times\s*\d+/,
|
||||
/\d+\s*divided\s*by\s*\d+/,
|
||||
/calculate/,
|
||||
/was\s+ist\s+\d+\s*[\+\-\*\/]\s*\d+/,
|
||||
/rechne/,
|
||||
/plus|minus|mal|geteilt|add|subtract|multiply|divide/
|
||||
];
|
||||
|
||||
// Simple factual questions - should guide to discovery
|
||||
const factualPatterns = [
|
||||
/what\s+color\s+is/,
|
||||
/how\s+many/,
|
||||
/what\s+day\s+is/,
|
||||
/what\s+time\s+is/,
|
||||
/welche\s+farbe/,
|
||||
/wie\s+viele/,
|
||||
/welcher\s+tag/,
|
||||
/wie\s+spät/
|
||||
];
|
||||
|
||||
if (mathPatterns.some(pattern => pattern.test(questionLower))) {
|
||||
console.log(`🧮 Detected mathematical question: "${question}"`);
|
||||
return 'mathematical';
|
||||
}
|
||||
|
||||
if (factualPatterns.some(pattern => pattern.test(questionLower))) {
|
||||
console.log(`📋 Detected simple factual question: "${question}"`);
|
||||
return 'factual_simple';
|
||||
}
|
||||
|
||||
// Default to exploratory for complex questions
|
||||
console.log(`🤔 Detected exploratory question: "${question}"`);
|
||||
return 'exploratory';
|
||||
}
|
||||
|
||||
// Function to get OpenAI-powered educational guidance (Primary)
|
||||
async function getOpenAIGuidance(question, language) {
|
||||
console.log('🤖 Calling OpenAI with GPT-3.5-turbo...');
|
||||
|
||||
const isGerman = language === 'de';
|
||||
const questionType = analyzeQuestionType(question);
|
||||
|
||||
let systemPrompt;
|
||||
let userPrompt;
|
||||
|
||||
if (questionType === 'mathematical') {
|
||||
systemPrompt = isGerman
|
||||
? "Du bist ein Mathe-Tutor für Kinder. Für einfache Rechenaufgaben führst du das Kind durch konkrete mathematische Schritte, aber verrate NIEMALS die finale Antwort. Erkläre das Rechensymbol, verwende einfache Beispiele, und lass das Kind selbst rechnen. Gib 2-3 Schritte die zum Rechnen anleiten, aber sage NIE das Endergebnis."
|
||||
: "You are a math tutor for children. For simple math problems, guide the child through concrete mathematical steps, but NEVER reveal the final answer. Explain the math symbol, use simple examples, and let the child calculate themselves. Provide 2-3 steps that guide calculation, but NEVER state the final result.";
|
||||
|
||||
userPrompt = isGerman
|
||||
? `Ein Kind fragt: "${question}". Gib 2-3 konkrete Mathe-Schritte die zum Rechnen anleiten. Erkläre was das Symbol bedeutet, verwende zählbare Beispiele, aber verrate NIEMALS die finale Antwort. Das Kind soll selbst rechnen und die Antwort finden.`
|
||||
: `A child asks: "${question}". Provide 2-3 concrete math steps that guide calculation. Explain what the symbol means, use countable examples, but NEVER reveal the final answer. The child should calculate and find the answer themselves.`;
|
||||
} else if (questionType === 'factual_simple') {
|
||||
systemPrompt = isGerman
|
||||
? "Du bist ein Lernbegleiter für Kinder. Bei einfachen Faktenfragen stellst du 2-3 spezifische, logische Schritte, die das Kind zur Antwort führen. Verwende konkrete Beispiele und Beobachtungen, die das Kind selbst machen kann. Keine allgemeinen Fragen, sondern spezifische Denkschritte."
|
||||
: "You are a learning companion for children. For simple factual questions, provide 2-3 specific, logical steps that lead the child to the answer. Use concrete examples and observations the child can make themselves. No general questions, but specific thinking steps.";
|
||||
|
||||
userPrompt = isGerman
|
||||
? `Ein Kind fragt: "${question}". Gib 2-3 konkrete Denkschritte, die spezifisch zu dieser Frage passen. Verwende Beispiele und lass das Kind selbst beobachten oder überlegen.`
|
||||
: `A child asks: "${question}". Provide 2-3 concrete thinking steps that are specific to this question. Use examples and have the child observe or think for themselves.`;
|
||||
} else {
|
||||
systemPrompt = isGerman
|
||||
? "Du bist ein pädagogischer Assistent für Kinder. Bei komplexeren Fragen stellst du durchdachte Leitfragen, die Kindern helfen, selbst über das Problem nachzudenken. Verwende einfache Sprache und konzentriere dich auf den Denkprozess."
|
||||
: "You are an educational assistant for children. For complex questions, provide thoughtful guiding questions that help children think through the problem themselves. Use simple language and focus on the thinking process.";
|
||||
|
||||
userPrompt = isGerman
|
||||
? `Ein Kind hat gefragt: "${question}". Hilf ihm dabei, selbst über die Antwort nachzudenken, indem du Leitfragen stellst.`
|
||||
: `A child asked: "${question}". Help them think through the answer themselves by providing guiding questions.`;
|
||||
}
|
||||
|
||||
try {
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: userPrompt }
|
||||
],
|
||||
max_tokens: 200,
|
||||
temperature: 0.7
|
||||
});
|
||||
|
||||
const aiResponse = completion.choices[0]?.message?.content || '';
|
||||
console.log('✅ OpenAI response received:', aiResponse.substring(0, 100) + '...');
|
||||
|
||||
// Parse the response into steps
|
||||
const steps = parseOpenAIResponseToSteps(aiResponse, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
questionType: questionType,
|
||||
steps: steps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: 'OpenAI GPT-3.5',
|
||||
showAnswerReveal: questionType === 'exploratory' // Only show reveal for complex questions
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.log('❌ OpenAI error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to parse OpenAI response into thinking steps
|
||||
function parseOpenAIResponseToSteps(text, language) {
|
||||
const lines = text.split('\n').filter(line => line.trim());
|
||||
const steps = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const trimmed = line.trim();
|
||||
// Look for numbered items or questions
|
||||
if (trimmed && (trimmed.match(/^\d+\./) || trimmed.includes('?') || trimmed.length > 10)) {
|
||||
// Clean up numbering and formatting
|
||||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '').trim();
|
||||
if (cleaned.length > 5) {
|
||||
steps.push({
|
||||
id: steps.length + 1,
|
||||
text: cleaned,
|
||||
type: 'question'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure we have at least 2 steps
|
||||
if (steps.length < 2) {
|
||||
const fallback = getFallbackGuidance('', language);
|
||||
return fallback.steps.slice(0, 3);
|
||||
}
|
||||
|
||||
return steps.slice(0, 4); // Limit to 4 steps max
|
||||
}
|
||||
|
||||
// Function to get Hugging Face guidance (Backup)
|
||||
async function getHuggingFaceGuidance(question, language) {
|
||||
console.log('🤖 Trying Hugging Face as backup...');
|
||||
|
||||
// Try each model until one works
|
||||
for (let i = 0; i < HF_MODELS.length; i++) {
|
||||
const model = HF_MODELS[i];
|
||||
const apiUrl = `https://api-inference.huggingface.co/models/${model}`;
|
||||
|
||||
try {
|
||||
console.log(`📡 Trying model ${i + 1}/${HF_MODELS.length}: ${model}`);
|
||||
|
||||
const educationalPrompt = `Help a child think about this question: "${question}".
|
||||
Don't give the answer. Instead, ask 3 guiding questions that help them discover it themselves.
|
||||
Format: 1. [question] 2. [question] 3. [question]`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': HF_API_TOKEN ? `Bearer ${HF_API_TOKEN}` : undefined,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
inputs: educationalPrompt,
|
||||
parameters: {
|
||||
max_length: 120,
|
||||
temperature: 0.8,
|
||||
do_sample: true,
|
||||
pad_token_id: 50256
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error && data.error.includes('loading')) {
|
||||
console.log(`⏳ Model ${model} is loading, trying next...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
console.log(`❌ Model ${model} error:`, data.error);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`✅ Success with model ${model}`);
|
||||
|
||||
let aiText = data[0]?.generated_text || '';
|
||||
|
||||
if (aiText.trim().length > 10) {
|
||||
const steps = parseHuggingFaceResponseToSteps(aiText, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
steps: steps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: `Hugging Face (${model.split('/')[1]})`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Model ${model} failed:`, error.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('All Hugging Face models are currently unavailable');
|
||||
}
|
||||
|
||||
// Function to parse Hugging Face response into thinking steps
|
||||
function parseHuggingFaceResponseToSteps(text, language) {
|
||||
const lines = text.split('\n').filter(line => line.trim());
|
||||
const steps = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed && trimmed.length > 10) {
|
||||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '');
|
||||
if (cleaned.length > 5) {
|
||||
steps.push({
|
||||
id: index + 1,
|
||||
text: cleaned,
|
||||
type: 'question'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (steps.length < 2) {
|
||||
const fallback = getFallbackGuidance('', language);
|
||||
return fallback.steps.slice(0, 3);
|
||||
}
|
||||
|
||||
return steps.slice(0, 4);
|
||||
}
|
||||
|
||||
// Updated main function that tries OpenAI first, then Hugging Face, then local fallback
|
||||
async function getAIGuidance(question, language) {
|
||||
// Try OpenAI first (most reliable)
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
try {
|
||||
return await getOpenAIGuidance(question, language);
|
||||
} catch (error) {
|
||||
console.log('⚠️ OpenAI failed, trying Hugging Face...');
|
||||
}
|
||||
}
|
||||
|
||||
// Try Hugging Face as backup
|
||||
if (process.env.HUGGING_FACE_TOKEN) {
|
||||
try {
|
||||
return await getHuggingFaceGuidance(question, language);
|
||||
} catch (error) {
|
||||
console.log('⚠️ Hugging Face failed, using local fallback...');
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback - this will throw error to trigger local guidance
|
||||
throw new Error('All AI services are currently unavailable');
|
||||
}
|
||||
|
||||
// Fallback guidance for when AI is unavailable
|
||||
function getFallbackGuidance(question, language) {
|
||||
const isGerman = language === 'de';
|
||||
|
||||
const fallbackSteps = isGerman ? [
|
||||
{ id: 1, text: "Was weißt du bereits über dieses Thema?", type: 'question' },
|
||||
{ id: 2, text: "Welche Teile der Frage verstehst du, und welche sind unklar?", type: 'question' },
|
||||
{ id: 3, text: "Wo könntest du mehr Informationen finden?", type: 'question' },
|
||||
{ id: 4, text: "Kannst du das Problem in kleinere Teile aufteilen?", type: 'question' }
|
||||
] : [
|
||||
{ id: 1, text: "What do you already know about this topic?", type: 'question' },
|
||||
{ id: 2, text: "Which parts of the question do you understand, and which are unclear?", type: 'question' },
|
||||
{ id: 3, text: "Where could you find more information about this?", type: 'question' },
|
||||
{ id: 4, text: "Can you break this problem down into smaller parts?", type: 'question' }
|
||||
];
|
||||
|
||||
return {
|
||||
type: 'rule-based',
|
||||
steps: fallbackSteps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: 'Educational Framework'
|
||||
};
|
||||
}
|
||||
|
||||
// Get random encouragement
|
||||
function getRandomEncouragement(language) {
|
||||
const encouragements = language === 'de' ? [
|
||||
"Großartige Frage! Du denkst wie ein echter Forscher! 🔬",
|
||||
"Super! Lass uns das zusammen herausfinden! 🚀",
|
||||
"Wow, das ist eine kluge Frage! 🤔",
|
||||
"Du bist auf dem richtigen Weg! 🌟"
|
||||
] : [
|
||||
"Great question! You're thinking like a real scientist! 🔬",
|
||||
"Awesome! Let's figure this out together! 🚀",
|
||||
"Wow, that's a smart question! 🤔",
|
||||
"You're on the right track! 🌟"
|
||||
];
|
||||
|
||||
return encouragements[Math.floor(Math.random() * encouragements.length)];
|
||||
}
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
ai_services: {
|
||||
openai: !!process.env.OPENAI_API_KEY,
|
||||
huggingface: !!process.env.HUGGING_FACE_TOKEN
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 KidsAI Explorer server running on port ${PORT}`);
|
||||
console.log(`📖 Visit http://localhost:${PORT} to start exploring!`);
|
||||
console.log(`🤖 AI Services: OpenAI=${!!process.env.OPENAI_API_KEY}, HuggingFace=${!!process.env.HUGGING_FACE_TOKEN}`);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
|
||||
// Function to get the actual answer (for answer reveal)
|
||||
async function getActualAnswer(question, language) {
|
||||
console.log('📝 Getting actual answer for:', question);
|
||||
|
||||
const isGerman = language === 'de';
|
||||
|
||||
const systemPrompt = isGerman
|
||||
? "Du bist ein hilfreicher Assistent für Kinder. Gib eine klare, einfache und altersgerechte Antwort auf die Frage. Verwende einfache Sprache und erkläre es so, dass ein Kind es verstehen kann. Halte die Antwort kurz aber vollständig."
|
||||
: "You are a helpful assistant for children. Provide a clear, simple, and age-appropriate answer to the question. Use simple language and explain it in a way a child can understand. Keep the answer concise but complete.";
|
||||
|
||||
const userPrompt = isGerman
|
||||
? `Ein Kind möchte wissen: "${question}". Gib eine einfache, klare Antwort.`
|
||||
: `A child wants to know: "${question}". Provide a simple, clear answer.`;
|
||||
|
||||
try {
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: userPrompt }
|
||||
],
|
||||
max_tokens: 150,
|
||||
temperature: 0.3 // Lower temperature for more factual answers
|
||||
});
|
||||
|
||||
const answer = completion.choices[0]?.message?.content || '';
|
||||
console.log('✅ Answer received from OpenAI');
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
text: answer.trim(),
|
||||
source: 'OpenAI GPT-3.5'
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('❌ OpenAI answer error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback answers for common questions
|
||||
function getFallbackAnswer(question, language) {
|
||||
const isGerman = language === 'de';
|
||||
|
||||
// Simple pattern matching for common questions
|
||||
const questionLower = question.toLowerCase();
|
||||
|
||||
if (questionLower.includes('sky') || questionLower.includes('himmel') || questionLower.includes('blue') || questionLower.includes('blau')) {
|
||||
return {
|
||||
type: 'rule-based',
|
||||
text: isGerman
|
||||
? "Der Himmel ist blau, weil winzig kleine Teilchen in der Luft das blaue Licht mehr streuen als andere Farben. Es ist wie wenn du blaues Licht durch viele kleine Glaskugeln scheinst!"
|
||||
: "The sky is blue because tiny particles in the air scatter blue light more than other colors. It's like shining blue light through many tiny glass balls!",
|
||||
source: 'Educational Framework'
|
||||
};
|
||||
}
|
||||
|
||||
if (questionLower.includes('bird') || questionLower.includes('fly') || questionLower.includes('vogel') || questionLower.includes('fliegen')) {
|
||||
return {
|
||||
type: 'rule-based',
|
||||
text: isGerman
|
||||
? "Vögel können fliegen, weil ihre Flügel eine besondere Form haben und sie sehr leichte, hohle Knochen haben. Sie schlagen mit den Flügeln und erzeugen Auftrieb, der sie in die Luft hebt!"
|
||||
: "Birds can fly because their wings have a special shape and they have very light, hollow bones. They flap their wings to create lift that pushes them up into the air!",
|
||||
source: 'Educational Framework'
|
||||
};
|
||||
}
|
||||
|
||||
if (questionLower.includes('1+1') || questionLower.includes('add') || questionLower.includes('plus')) {
|
||||
return {
|
||||
type: 'rule-based',
|
||||
text: isGerman
|
||||
? "1 + 1 = 2! Wenn du einen Apfel hast und noch einen dazu bekommst, hast du zwei Äpfel. Das ist Addition - zusammenzählen!"
|
||||
: "1 + 1 = 2! When you have one apple and get another one, you have two apples. That's addition - adding together!",
|
||||
source: 'Educational Framework'
|
||||
};
|
||||
}
|
||||
|
||||
// Generic fallback
|
||||
return {
|
||||
type: 'rule-based',
|
||||
text: isGerman
|
||||
? "Das ist eine tolle Frage! Die Antwort hängt von vielen Faktoren ab. Ich empfehle dir, in einem Buch nachzuschauen oder einen Erwachsenen zu fragen, der mehr darüber weiß."
|
||||
: "That's a great question! The answer depends on many factors. I recommend looking it up in a book or asking an adult who knows more about this topic.",
|
||||
source: 'Educational Framework'
|
||||
};
|
||||
}
|
||||
|
||||
// Generate additional math hints using AI
|
||||
async function generateAdditionalMathHints(question, language, previousHints) {
|
||||
const isGerman = language === 'de';
|
||||
|
||||
const systemPrompt = isGerman
|
||||
? "Du bist ein geduldiger Mathe-Tutor für Kinder. Das Kind hat bereits Schwierigkeiten mit einer Rechenaufgabe. Gib 2-3 zusätzliche, sehr einfache und konkrete Hilfestellungen. Verwende noch einfachere Beispiele, zerlege die Aufgabe in kleinere Schritte, oder verwende visuelle Hilfsmittel wie Finger oder Gegenstände. Sei ermutigend und geduldig."
|
||||
: "You are a patient math tutor for children. The child is already struggling with a math problem. Provide 2-3 additional, very simple and concrete hints. Use even simpler examples, break the problem into smaller steps, or use visual aids like fingers or objects. Be encouraging and patient.";
|
||||
|
||||
const userPrompt = isGerman
|
||||
? `Ein Kind hat Schwierigkeiten mit: "${question}". Es braucht zusätzliche, einfachere Hilfe. Gib 2-3 sehr konkrete, einfache Schritte mit visuellen Hilfsmitteln oder noch einfacheren Beispielen.`
|
||||
: `A child is struggling with: "${question}". They need additional, simpler help. Provide 2-3 very concrete, simple steps with visual aids or even simpler examples.`;
|
||||
|
||||
try {
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: userPrompt }
|
||||
],
|
||||
max_tokens: 200,
|
||||
temperature: 0.7
|
||||
});
|
||||
|
||||
const aiResponse = completion.choices[0]?.message?.content || '';
|
||||
const steps = parseOpenAIResponseToSteps(aiResponse, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
steps: steps,
|
||||
encouragement: isGerman ? "Keine Sorge, wir schaffen das zusammen! 💪" : "Don't worry, we can do this together! 💪",
|
||||
source: 'OpenAI GPT-3.5'
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.log('❌ Additional hints AI error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback hints for when AI is not available
|
||||
function getFallbackMathHints(question, language) {
|
||||
const isGerman = language === 'de';
|
||||
const questionLower = question.toLowerCase();
|
||||
|
||||
// Determine operation type and generate appropriate hints
|
||||
let hints = [];
|
||||
|
||||
if (questionLower.includes('+') || questionLower.includes('plus')) {
|
||||
hints = isGerman ? [
|
||||
"Verwende deine Finger zum Zählen - das macht es einfacher!",
|
||||
"Nimm die erste Zahl und zähle dann die zweite Zahl dazu.",
|
||||
"Du kannst auch kleine Gegenstände wie Münzen oder Stifte zum Zählen verwenden."
|
||||
] : [
|
||||
"Use your fingers for counting - it makes it easier!",
|
||||
"Take the first number and count the second number on top of it.",
|
||||
"You can also use small objects like coins or pencils for counting."
|
||||
];
|
||||
} else if (questionLower.includes('-') || questionLower.includes('minus')) {
|
||||
hints = isGerman ? [
|
||||
"Bei Minus nehmen wir etwas weg. Starte mit der ersten Zahl.",
|
||||
"Zähle rückwärts um die zweite Zahl.",
|
||||
"Stelle dir vor, du hast Süßigkeiten und gibst einige weg."
|
||||
] : [
|
||||
"With minus, we take something away. Start with the first number.",
|
||||
"Count backwards by the second number.",
|
||||
"Imagine you have candies and you give some away."
|
||||
];
|
||||
} else {
|
||||
// Generic math hints
|
||||
hints = isGerman ? [
|
||||
"Lass uns das Problem in kleinere Teile aufteilen.",
|
||||
"Verwende deine Finger oder male kleine Kreise zum Zählen.",
|
||||
"Nimm dir Zeit - Mathe ist wie ein Puzzle, das wir zusammen lösen."
|
||||
] : [
|
||||
"Let's break the problem into smaller parts.",
|
||||
"Use your fingers or draw small circles for counting.",
|
||||
"Take your time - math is like a puzzle we solve together."
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'fallback',
|
||||
steps: hints.map((hint, index) => ({
|
||||
id: index + 1,
|
||||
text: hint,
|
||||
type: 'hint'
|
||||
})),
|
||||
encouragement: isGerman ? "Du schaffst das! 🌟" : "You can do it! 🌟",
|
||||
source: 'Fallback'
|
||||
};
|
||||
}
|
||||
417
html/kidsai/server_backup.js
Executable file
417
html/kidsai/server_backup.js
Executable file
@@ -0,0 +1,417 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const fetch = require('node-fetch');
|
||||
const OpenAI = require('openai');
|
||||
|
||||
// Load environment variables from .env file
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3002;
|
||||
|
||||
// OpenAI Configuration (Primary AI service)
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY
|
||||
});
|
||||
|
||||
// Hugging Face API configuration (Backup)
|
||||
const HF_MODELS = [
|
||||
'microsoft/DialoGPT-small', // Smaller, more reliable model
|
||||
'google/flan-t5-small', // Good for instructions
|
||||
'facebook/blenderbot-400M-distill', // Conversational AI
|
||||
'microsoft/DialoGPT-medium' // Original choice
|
||||
];
|
||||
|
||||
const HF_API_TOKEN = process.env.HUGGING_FACE_TOKEN;
|
||||
|
||||
// Educational prompts for AI to guide thinking instead of giving direct answers
|
||||
const EDUCATIONAL_PROMPTS = {
|
||||
en: {
|
||||
systemPrompt: "You are an educational assistant for children. Instead of giving direct answers, ask 2-3 guiding questions that help children think through the problem themselves. Be encouraging and use simple language. Focus on the thinking process, not the answer.",
|
||||
prefix: "That's a great question! Let me help you think through this step by step. Instead of telling you the answer, here are some questions to guide your thinking:"
|
||||
},
|
||||
de: {
|
||||
systemPrompt: "Du bist ein Lernassistent für Kinder. Anstatt direkte Antworten zu geben, stelle 2-3 Leitfragen, die Kindern helfen, das Problem selbst zu durchdenken. Sei ermutigend und verwende einfache Sprache. Konzentriere dich auf den Denkprozess, nicht auf die Antwort.",
|
||||
prefix: "Das ist eine tolle Frage! Lass mich dir helfen, Schritt für Schritt darüber nachzudenken. Anstatt dir die Antwort zu sagen, hier sind einige Fragen, die dein Denken leiten:"
|
||||
}
|
||||
};
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.static('.'));
|
||||
|
||||
// Serve the main page
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'index.html'));
|
||||
});
|
||||
|
||||
// API endpoint for AI-powered educational guidance
|
||||
app.post('/api/ask', async (req, res) => {
|
||||
const { question, language = 'en' } = req.body;
|
||||
|
||||
if (!question || question.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Question is required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Get AI-powered guidance
|
||||
const aiGuidance = await getAIGuidance(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
guidance: aiGuidance,
|
||||
question: question,
|
||||
language: language
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('AI API Error:', error);
|
||||
|
||||
// Fallback to rule-based guidance if AI fails
|
||||
const fallbackGuidance = getFallbackGuidance(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
guidance: fallbackGuidance,
|
||||
question: question,
|
||||
language: language,
|
||||
fallback: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Function to get OpenAI-powered educational guidance (Primary)
|
||||
async function getOpenAIGuidance(question, language) {
|
||||
console.log('🤖 Calling OpenAI with GPT-3.5-turbo...');
|
||||
|
||||
const isGerman = language === 'de';
|
||||
|
||||
const systemPrompt = isGerman
|
||||
? "Du bist ein pädagogischer Assistent für Kinder. Anstatt direkte Antworten zu geben, stellst du 3-4 durchdachte Leitfragen, die Kindern helfen, selbst über das Problem nachzudenken. Verwende einfache Sprache, sei ermutigend und konzentriere dich auf den Denkprozess. Formatiere als nummerierte Liste."
|
||||
: "You are an educational assistant for children. Instead of giving direct answers, provide 3-4 thoughtful guiding questions that help children think through the problem themselves. Use simple language, be encouraging, and focus on the thinking process. Format as a numbered list.";
|
||||
|
||||
const userPrompt = isGerman
|
||||
? `Ein Kind hat gefragt: "${question}". Hilf ihm dabei, selbst über die Antwort nachzudenken, indem du Leitfragen stellst.`
|
||||
: `A child asked: "${question}". Help them think through the answer themselves by providing guiding questions.`;
|
||||
|
||||
try {
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: userPrompt }
|
||||
],
|
||||
max_tokens: 200,
|
||||
temperature: 0.7
|
||||
});
|
||||
|
||||
const aiResponse = completion.choices[0]?.message?.content || '';
|
||||
console.log('✅ OpenAI response received:', aiResponse.substring(0, 100) + '...');
|
||||
|
||||
// Parse the response into steps
|
||||
const steps = parseOpenAIResponseToSteps(aiResponse, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
steps: steps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: 'OpenAI GPT-3.5'
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.log('❌ OpenAI error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to parse OpenAI response into thinking steps
|
||||
function parseOpenAIResponseToSteps(text, language) {
|
||||
const lines = text.split('\n').filter(line => line.trim());
|
||||
const steps = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const trimmed = line.trim();
|
||||
// Look for numbered items or questions
|
||||
if (trimmed && (trimmed.match(/^\d+\./) || trimmed.includes('?') || trimmed.length > 10)) {
|
||||
// Clean up numbering and formatting
|
||||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '').trim();
|
||||
if (cleaned.length > 5) {
|
||||
steps.push({
|
||||
id: steps.length + 1,
|
||||
text: cleaned,
|
||||
type: 'question'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure we have at least 2 steps
|
||||
if (steps.length < 2) {
|
||||
const fallback = getFallbackGuidance('', language);
|
||||
return fallback.steps.slice(0, 3);
|
||||
}
|
||||
|
||||
return steps.slice(0, 4); // Limit to 4 steps max
|
||||
}
|
||||
|
||||
// Function to get AI-powered educational guidance with model fallbacks (Backup)
|
||||
async function getHuggingFaceGuidance(question, language) {
|
||||
console.log('🤖 Trying Hugging Face as backup...');
|
||||
|
||||
// Try each model until one works
|
||||
for (let i = 0; i < HF_MODELS.length; i++) {
|
||||
const model = HF_MODELS[i];
|
||||
const apiUrl = `https://api-inference.huggingface.co/models/${model}`;
|
||||
|
||||
try {
|
||||
console.log(`📡 Trying model ${i + 1}/${HF_MODELS.length}: ${model}`);
|
||||
|
||||
// Create educational prompt
|
||||
const educationalPrompt = `Help a child think about this question: "${question}".
|
||||
Don't give the answer. Instead, ask 3 guiding questions that help them discover it themselves.
|
||||
Format: 1. [question] 2. [question] 3. [question]`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': HF_API_TOKEN ? `Bearer ${HF_API_TOKEN}` : undefined,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
inputs: educationalPrompt,
|
||||
parameters: {
|
||||
max_length: 120,
|
||||
temperature: 0.8,
|
||||
do_sample: true,
|
||||
pad_token_id: 50256
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
// Check if the model is still loading
|
||||
if (data.error && data.error.includes('loading')) {
|
||||
console.log(`⏳ Model ${model} is loading, trying next...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
console.log(`❌ Model ${model} error:`, data.error);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`✅ Success with model ${model}`);
|
||||
|
||||
// Extract and format the AI response
|
||||
let aiText = data[0]?.generated_text || '';
|
||||
|
||||
if (aiText.trim().length > 10) {
|
||||
const steps = parseAIResponseToSteps(aiText, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
steps: steps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: `Hugging Face (${model.split('/')[1]})`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Model ${model} failed:`, error.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If all models fail, throw error to trigger fallback
|
||||
throw new Error('All Hugging Face models are currently unavailable');
|
||||
}
|
||||
|
||||
// Updated main function that tries OpenAI first, then Hugging Face, then local fallback
|
||||
async function getAIGuidance(question, language) {
|
||||
// Try OpenAI first (most reliable)
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
try {
|
||||
return await getOpenAIGuidance(question, language);
|
||||
} catch (error) {
|
||||
console.log('⚠️ OpenAI failed, trying Hugging Face...');
|
||||
}
|
||||
}
|
||||
|
||||
// Try Hugging Face as backup
|
||||
if (process.env.HUGGING_FACE_TOKEN) {
|
||||
try {
|
||||
return await getHuggingFaceGuidance(question, language);
|
||||
} catch (error) {
|
||||
console.log('⚠️ Hugging Face failed, using local fallback...');
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback - this will throw error to trigger local guidance
|
||||
throw new Error('All AI services are currently unavailable');
|
||||
}
|
||||
|
||||
// Try each model until one works
|
||||
for (let i = 0; i < HF_MODELS.length; i++) {
|
||||
const model = HF_MODELS[i];
|
||||
const apiUrl = `https://api-inference.huggingface.co/models/${model}`;
|
||||
|
||||
try {
|
||||
console.log(`📡 Trying model ${i + 1}/${HF_MODELS.length}: ${model}`);
|
||||
|
||||
// Create educational prompt
|
||||
const educationalPrompt = `Help a child think about this question: "${question}".
|
||||
Don't give the answer. Instead, ask 3 guiding questions that help them discover it themselves.
|
||||
Format: 1. [question] 2. [question] 3. [question]`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': HF_API_TOKEN ? `Bearer ${HF_API_TOKEN}` : undefined,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
inputs: educationalPrompt,
|
||||
parameters: {
|
||||
max_length: 120,
|
||||
temperature: 0.8,
|
||||
do_sample: true,
|
||||
pad_token_id: 50256
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
console.log(`<EFBFBD> Model ${model} response status:`, response.status);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
// Check if the model is still loading
|
||||
if (data.error && data.error.includes('loading')) {
|
||||
console.log(`⏳ Model ${model} is loading, trying next...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
console.log(`❌ Model ${model} error:`, data.error);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`✅ Success with model ${model}:`, JSON.stringify(data, null, 2));
|
||||
|
||||
// Extract and format the AI response
|
||||
let aiText = data[0]?.generated_text || '';
|
||||
|
||||
if (aiText.trim().length > 10) {
|
||||
const steps = parseAIResponseToSteps(aiText, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
steps: steps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: `Hugging Face AI (${model.split('/')[1]})`
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
console.log(`❌ Model ${model} HTTP error ${response.status}:`, errorText);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Model ${model} failed:`, error.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If all models fail, throw error to trigger fallback
|
||||
throw new Error('All AI models are currently unavailable');
|
||||
}
|
||||
|
||||
// Function to parse AI response into thinking steps
|
||||
function parseAIResponseToSteps(text, language) {
|
||||
const lines = text.split('\n').filter(line => line.trim());
|
||||
const steps = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed && trimmed.length > 10) {
|
||||
// Remove numbering if present
|
||||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '');
|
||||
if (cleaned.length > 5) {
|
||||
steps.push({
|
||||
id: index + 1,
|
||||
text: cleaned,
|
||||
type: 'question'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure we have at least 2 steps
|
||||
if (steps.length < 2) {
|
||||
const fallback = getFallbackGuidance('', language);
|
||||
return fallback.steps.slice(0, 3);
|
||||
}
|
||||
|
||||
return steps.slice(0, 4); // Limit to 4 steps max
|
||||
}
|
||||
|
||||
// Fallback guidance for when AI is unavailable
|
||||
function getFallbackGuidance(question, language) {
|
||||
const isGerman = language === 'de';
|
||||
|
||||
const fallbackSteps = isGerman ? [
|
||||
{ id: 1, text: "Was weißt du bereits über dieses Thema?", type: 'question' },
|
||||
{ id: 2, text: "Welche Teile der Frage verstehst du, und welche sind unklar?", type: 'question' },
|
||||
{ id: 3, text: "Wo könntest du mehr Informationen finden?", type: 'question' },
|
||||
{ id: 4, text: "Kannst du das Problem in kleinere Teile aufteilen?", type: 'question' }
|
||||
] : [
|
||||
{ id: 1, text: "What do you already know about this topic?", type: 'question' },
|
||||
{ id: 2, text: "Which parts of the question do you understand, and which are unclear?", type: 'question' },
|
||||
{ id: 3, text: "Where could you find more information about this?", type: 'question' },
|
||||
{ id: 4, text: "Can you break this problem down into smaller parts?", type: 'question' }
|
||||
];
|
||||
|
||||
return {
|
||||
type: 'rule-based',
|
||||
steps: fallbackSteps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: 'Educational Framework'
|
||||
};
|
||||
}
|
||||
|
||||
// Get random encouragement
|
||||
function getRandomEncouragement(language) {
|
||||
const encouragements = language === 'de' ? [
|
||||
"Großartige Frage! Du denkst wie ein echter Forscher! 🔬",
|
||||
"Super! Lass uns das zusammen herausfinden! 🚀",
|
||||
"Wow, das ist eine kluge Frage! 🤔",
|
||||
"Du bist auf dem richtigen Weg! 🌟"
|
||||
] : [
|
||||
"Great question! You're thinking like a real scientist! 🔬",
|
||||
"Awesome! Let's figure this out together! 🚀",
|
||||
"Wow, that's a smart question! 🤔",
|
||||
"You're on the right track! 🌟"
|
||||
];
|
||||
|
||||
return encouragements[Math.floor(Math.random() * encouragements.length)];
|
||||
}
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 KidsAI Explorer server running on port ${PORT}`);
|
||||
console.log(`📖 Visit http://localhost:${PORT} to start exploring!`);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
343
html/kidsai/server_new.js
Normal file
343
html/kidsai/server_new.js
Normal file
@@ -0,0 +1,343 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const fetch = require('node-fetch');
|
||||
const OpenAI = require('openai');
|
||||
|
||||
// Load environment variables from .env file
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3002;
|
||||
|
||||
// OpenAI Configuration (Primary AI service)
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY
|
||||
});
|
||||
|
||||
// Hugging Face API configuration (Backup)
|
||||
const HF_MODELS = [
|
||||
'microsoft/DialoGPT-small',
|
||||
'google/flan-t5-small',
|
||||
'facebook/blenderbot-400M-distill',
|
||||
'microsoft/DialoGPT-medium'
|
||||
];
|
||||
|
||||
const HF_API_TOKEN = process.env.HUGGING_FACE_TOKEN;
|
||||
|
||||
// Educational prompts for AI to guide thinking instead of giving direct answers
|
||||
const EDUCATIONAL_PROMPTS = {
|
||||
en: {
|
||||
systemPrompt: "You are an educational assistant for children. Instead of giving direct answers, ask 2-3 guiding questions that help children think through the problem themselves. Be encouraging and use simple language. Focus on the thinking process, not the answer.",
|
||||
prefix: "That's a great question! Let me help you think through this step by step. Instead of telling you the answer, here are some questions to guide your thinking:"
|
||||
},
|
||||
de: {
|
||||
systemPrompt: "Du bist ein Lernassistent für Kinder. Anstatt direkte Antworten zu geben, stelle 2-3 Leitfragen, die Kindern helfen, das Problem selbst zu durchdenken. Sei ermutigend und verwende einfache Sprache. Konzentriere dich auf den Denkprozess, nicht auf die Antwort.",
|
||||
prefix: "Das ist eine tolle Frage! Lass mich dir helfen, Schritt für Schritt darüber nachzudenken. Anstatt dir die Antwort zu sagen, hier sind einige Fragen, die dein Denken leiten:"
|
||||
}
|
||||
};
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.static('.'));
|
||||
|
||||
// Serve the main page
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'index.html'));
|
||||
});
|
||||
|
||||
// API endpoint for AI-powered educational guidance
|
||||
app.post('/api/ask', async (req, res) => {
|
||||
const { question, language = 'en' } = req.body;
|
||||
|
||||
if (!question || question.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Question is required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Get AI-powered guidance
|
||||
const aiGuidance = await getAIGuidance(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
guidance: aiGuidance,
|
||||
question: question,
|
||||
language: language
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('AI API Error:', error);
|
||||
|
||||
// Fallback to rule-based guidance if AI fails
|
||||
const fallbackGuidance = getFallbackGuidance(question, language);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
guidance: fallbackGuidance,
|
||||
question: question,
|
||||
language: language,
|
||||
fallback: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Function to get OpenAI-powered educational guidance (Primary)
|
||||
async function getOpenAIGuidance(question, language) {
|
||||
console.log('🤖 Calling OpenAI with GPT-3.5-turbo...');
|
||||
|
||||
const isGerman = language === 'de';
|
||||
|
||||
const systemPrompt = isGerman
|
||||
? "Du bist ein pädagogischer Assistent für Kinder. Anstatt direkte Antworten zu geben, stellst du 3-4 durchdachte Leitfragen, die Kindern helfen, selbst über das Problem nachzudenken. Verwende einfache Sprache, sei ermutigend und konzentriere dich auf den Denkprozess. Formatiere als nummerierte Liste."
|
||||
: "You are an educational assistant for children. Instead of giving direct answers, provide 3-4 thoughtful guiding questions that help children think through the problem themselves. Use simple language, be encouraging, and focus on the thinking process. Format as a numbered list.";
|
||||
|
||||
const userPrompt = isGerman
|
||||
? `Ein Kind hat gefragt: "${question}". Hilf ihm dabei, selbst über die Antwort nachzudenken, indem du Leitfragen stellst.`
|
||||
: `A child asked: "${question}". Help them think through the answer themselves by providing guiding questions.`;
|
||||
|
||||
try {
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: "gpt-3.5-turbo",
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: userPrompt }
|
||||
],
|
||||
max_tokens: 200,
|
||||
temperature: 0.7
|
||||
});
|
||||
|
||||
const aiResponse = completion.choices[0]?.message?.content || '';
|
||||
console.log('✅ OpenAI response received:', aiResponse.substring(0, 100) + '...');
|
||||
|
||||
// Parse the response into steps
|
||||
const steps = parseOpenAIResponseToSteps(aiResponse, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
steps: steps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: 'OpenAI GPT-3.5'
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.log('❌ OpenAI error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to parse OpenAI response into thinking steps
|
||||
function parseOpenAIResponseToSteps(text, language) {
|
||||
const lines = text.split('\n').filter(line => line.trim());
|
||||
const steps = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const trimmed = line.trim();
|
||||
// Look for numbered items or questions
|
||||
if (trimmed && (trimmed.match(/^\d+\./) || trimmed.includes('?') || trimmed.length > 10)) {
|
||||
// Clean up numbering and formatting
|
||||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '').trim();
|
||||
if (cleaned.length > 5) {
|
||||
steps.push({
|
||||
id: steps.length + 1,
|
||||
text: cleaned,
|
||||
type: 'question'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure we have at least 2 steps
|
||||
if (steps.length < 2) {
|
||||
const fallback = getFallbackGuidance('', language);
|
||||
return fallback.steps.slice(0, 3);
|
||||
}
|
||||
|
||||
return steps.slice(0, 4); // Limit to 4 steps max
|
||||
}
|
||||
|
||||
// Function to get Hugging Face guidance (Backup)
|
||||
async function getHuggingFaceGuidance(question, language) {
|
||||
console.log('🤖 Trying Hugging Face as backup...');
|
||||
|
||||
// Try each model until one works
|
||||
for (let i = 0; i < HF_MODELS.length; i++) {
|
||||
const model = HF_MODELS[i];
|
||||
const apiUrl = `https://api-inference.huggingface.co/models/${model}`;
|
||||
|
||||
try {
|
||||
console.log(`📡 Trying model ${i + 1}/${HF_MODELS.length}: ${model}`);
|
||||
|
||||
const educationalPrompt = `Help a child think about this question: "${question}".
|
||||
Don't give the answer. Instead, ask 3 guiding questions that help them discover it themselves.
|
||||
Format: 1. [question] 2. [question] 3. [question]`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': HF_API_TOKEN ? `Bearer ${HF_API_TOKEN}` : undefined,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
inputs: educationalPrompt,
|
||||
parameters: {
|
||||
max_length: 120,
|
||||
temperature: 0.8,
|
||||
do_sample: true,
|
||||
pad_token_id: 50256
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error && data.error.includes('loading')) {
|
||||
console.log(`⏳ Model ${model} is loading, trying next...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
console.log(`❌ Model ${model} error:`, data.error);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`✅ Success with model ${model}`);
|
||||
|
||||
let aiText = data[0]?.generated_text || '';
|
||||
|
||||
if (aiText.trim().length > 10) {
|
||||
const steps = parseHuggingFaceResponseToSteps(aiText, language);
|
||||
|
||||
return {
|
||||
type: 'ai-powered',
|
||||
steps: steps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: `Hugging Face (${model.split('/')[1]})`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Model ${model} failed:`, error.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('All Hugging Face models are currently unavailable');
|
||||
}
|
||||
|
||||
// Function to parse Hugging Face response into thinking steps
|
||||
function parseHuggingFaceResponseToSteps(text, language) {
|
||||
const lines = text.split('\n').filter(line => line.trim());
|
||||
const steps = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed && trimmed.length > 10) {
|
||||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '');
|
||||
if (cleaned.length > 5) {
|
||||
steps.push({
|
||||
id: index + 1,
|
||||
text: cleaned,
|
||||
type: 'question'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (steps.length < 2) {
|
||||
const fallback = getFallbackGuidance('', language);
|
||||
return fallback.steps.slice(0, 3);
|
||||
}
|
||||
|
||||
return steps.slice(0, 4);
|
||||
}
|
||||
|
||||
// Updated main function that tries OpenAI first, then Hugging Face, then local fallback
|
||||
async function getAIGuidance(question, language) {
|
||||
// Try OpenAI first (most reliable)
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
try {
|
||||
return await getOpenAIGuidance(question, language);
|
||||
} catch (error) {
|
||||
console.log('⚠️ OpenAI failed, trying Hugging Face...');
|
||||
}
|
||||
}
|
||||
|
||||
// Try Hugging Face as backup
|
||||
if (process.env.HUGGING_FACE_TOKEN) {
|
||||
try {
|
||||
return await getHuggingFaceGuidance(question, language);
|
||||
} catch (error) {
|
||||
console.log('⚠️ Hugging Face failed, using local fallback...');
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback - this will throw error to trigger local guidance
|
||||
throw new Error('All AI services are currently unavailable');
|
||||
}
|
||||
|
||||
// Fallback guidance for when AI is unavailable
|
||||
function getFallbackGuidance(question, language) {
|
||||
const isGerman = language === 'de';
|
||||
|
||||
const fallbackSteps = isGerman ? [
|
||||
{ id: 1, text: "Was weißt du bereits über dieses Thema?", type: 'question' },
|
||||
{ id: 2, text: "Welche Teile der Frage verstehst du, und welche sind unklar?", type: 'question' },
|
||||
{ id: 3, text: "Wo könntest du mehr Informationen finden?", type: 'question' },
|
||||
{ id: 4, text: "Kannst du das Problem in kleinere Teile aufteilen?", type: 'question' }
|
||||
] : [
|
||||
{ id: 1, text: "What do you already know about this topic?", type: 'question' },
|
||||
{ id: 2, text: "Which parts of the question do you understand, and which are unclear?", type: 'question' },
|
||||
{ id: 3, text: "Where could you find more information about this?", type: 'question' },
|
||||
{ id: 4, text: "Can you break this problem down into smaller parts?", type: 'question' }
|
||||
];
|
||||
|
||||
return {
|
||||
type: 'rule-based',
|
||||
steps: fallbackSteps,
|
||||
encouragement: getRandomEncouragement(language),
|
||||
source: 'Educational Framework'
|
||||
};
|
||||
}
|
||||
|
||||
// Get random encouragement
|
||||
function getRandomEncouragement(language) {
|
||||
const encouragements = language === 'de' ? [
|
||||
"Großartige Frage! Du denkst wie ein echter Forscher! 🔬",
|
||||
"Super! Lass uns das zusammen herausfinden! 🚀",
|
||||
"Wow, das ist eine kluge Frage! 🤔",
|
||||
"Du bist auf dem richtigen Weg! 🌟"
|
||||
] : [
|
||||
"Great question! You're thinking like a real scientist! 🔬",
|
||||
"Awesome! Let's figure this out together! 🚀",
|
||||
"Wow, that's a smart question! 🤔",
|
||||
"You're on the right track! 🌟"
|
||||
];
|
||||
|
||||
return encouragements[Math.floor(Math.random() * encouragements.length)];
|
||||
}
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
ai_services: {
|
||||
openai: !!process.env.OPENAI_API_KEY,
|
||||
huggingface: !!process.env.HUGGING_FACE_TOKEN
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 KidsAI Explorer server running on port ${PORT}`);
|
||||
console.log(`📖 Visit http://localhost:${PORT} to start exploring!`);
|
||||
console.log(`🤖 AI Services: OpenAI=${!!process.env.OPENAI_API_KEY}, HuggingFace=${!!process.env.HUGGING_FACE_TOKEN}`);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
158
html/kidsai/test-ai.html
Normal file
158
html/kidsai/test-ai.html
Normal file
@@ -0,0 +1,158 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>KidsAI Explorer - AI Test</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
||||
.test-section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
|
||||
.result { background: #f0f0f0; padding: 10px; margin: 10px 0; border-radius: 4px; }
|
||||
.success { background: #d4edda; border-color: #c3e6cb; color: #155724; }
|
||||
.error { background: #f8d7da; border-color: #f5c6cb; color: #721c24; }
|
||||
.loading { color: #666; }
|
||||
button { padding: 8px 16px; margin: 5px; cursor: pointer; }
|
||||
input[type="text"] { width: 100%; padding: 8px; margin: 10px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🤖 KidsAI Explorer - AI Integration Test</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>API Health Check</h2>
|
||||
<button onclick="testHealth()">Test Health Endpoint</button>
|
||||
<div id="health-result" class="result loading">Click button to test...</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>AI Question Test</h2>
|
||||
<input type="text" id="test-question" placeholder="Ask a question (e.g., Why is the sky blue?)" value="Why is the sky blue?">
|
||||
<button onclick="testAI('en')">Test AI (English)</button>
|
||||
<button onclick="testAI('de')">Test AI (German)</button>
|
||||
<div id="ai-result" class="result loading">Click button to test AI...</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Fallback Test</h2>
|
||||
<button onclick="testFallback()">Test Fallback Mode</button>
|
||||
<div id="fallback-result" class="result loading">Click button to test fallback...</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_BASE = '/api';
|
||||
|
||||
async function testHealth() {
|
||||
const resultDiv = document.getElementById('health-result');
|
||||
resultDiv.className = 'result loading';
|
||||
resultDiv.textContent = 'Testing health endpoint...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/health`);
|
||||
const data = await response.json();
|
||||
|
||||
resultDiv.className = 'result success';
|
||||
resultDiv.innerHTML = `
|
||||
<strong>✅ Health Check Passed!</strong><br>
|
||||
Status: ${data.status}<br>
|
||||
Timestamp: ${data.timestamp}
|
||||
`;
|
||||
} catch (error) {
|
||||
resultDiv.className = 'result error';
|
||||
resultDiv.innerHTML = `
|
||||
<strong>❌ Health Check Failed!</strong><br>
|
||||
Error: ${error.message}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testAI(language = 'en') {
|
||||
const resultDiv = document.getElementById('ai-result');
|
||||
const question = document.getElementById('test-question').value;
|
||||
|
||||
resultDiv.className = 'result loading';
|
||||
resultDiv.textContent = 'Getting AI guidance...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/ask`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: question,
|
||||
language: language
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
resultDiv.className = 'result success';
|
||||
resultDiv.innerHTML = `
|
||||
<strong>✅ AI Response Received!</strong><br>
|
||||
<strong>Question:</strong> ${data.question}<br>
|
||||
<strong>Language:</strong> ${data.language}<br>
|
||||
<strong>Type:</strong> ${data.guidance.type}<br>
|
||||
<strong>Source:</strong> ${data.guidance.source}<br>
|
||||
${data.fallback ? '<strong>⚠️ Fallback Mode</strong><br>' : ''}
|
||||
<strong>Encouragement:</strong> ${data.guidance.encouragement}<br>
|
||||
<strong>Thinking Steps:</strong><br>
|
||||
<ol>
|
||||
${data.guidance.steps.map(step => `<li>${step.text}</li>`).join('')}
|
||||
</ol>
|
||||
`;
|
||||
} else {
|
||||
resultDiv.className = 'result error';
|
||||
resultDiv.textContent = `❌ Error: ${data.error}`;
|
||||
}
|
||||
} catch (error) {
|
||||
resultDiv.className = 'result error';
|
||||
resultDiv.innerHTML = `
|
||||
<strong>❌ Request Failed!</strong><br>
|
||||
Error: ${error.message}<br>
|
||||
<small>This might be expected if Hugging Face API is rate-limited or unavailable.</small>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testFallback() {
|
||||
// Force fallback by using an invalid API endpoint temporarily
|
||||
const resultDiv = document.getElementById('fallback-result');
|
||||
resultDiv.className = 'result loading';
|
||||
resultDiv.textContent = 'Testing fallback mode...';
|
||||
|
||||
try {
|
||||
// Test with a simple question that should trigger fallback logic
|
||||
const response = await fetch(`${API_BASE}/ask`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: "Test fallback question",
|
||||
language: 'en'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
resultDiv.className = 'result success';
|
||||
resultDiv.innerHTML = `
|
||||
<strong>✅ Fallback Test Results:</strong><br>
|
||||
<strong>Is Fallback:</strong> ${data.fallback ? 'Yes' : 'No'}<br>
|
||||
<strong>Type:</strong> ${data.guidance.type}<br>
|
||||
<strong>Source:</strong> ${data.guidance.source}<br>
|
||||
<strong>Steps Count:</strong> ${data.guidance.steps.length}<br>
|
||||
<strong>Sample Step:</strong> ${data.guidance.steps[0]?.text || 'None'}
|
||||
`;
|
||||
} catch (error) {
|
||||
resultDiv.className = 'result error';
|
||||
resultDiv.textContent = `❌ Fallback test failed: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-test health on page load
|
||||
window.onload = () => testHealth();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user