Add LangChain support, new UI components, and schema migration
- Add @langchain packages and zod for schema validation - Introduce HubertChat component and Astro sections - Create initial DB migration for Hubert data - Update API routes for chat, conversations, and new visitors - Adjust package.json and pnpm-lock with new dependencies Hubert The Eunuch
This commit is contained in:
parent
bd09fbe5f8
commit
e1a1a4259a
@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
active: true
|
|
||||||
iteration: 1
|
|
||||||
max_iterations: 100
|
|
||||||
completion_promise: "DONE"
|
|
||||||
started_at: "2026-01-03T03:44:54.441Z"
|
|
||||||
session_id: "ses_47e0ab7d0ffeVYIumRnRO0IG1n"
|
|
||||||
---
|
|
||||||
Complete the task as instructed
|
|
||||||
@ -22,6 +22,10 @@
|
|||||||
"@astrojs/react": "^4.4.2",
|
"@astrojs/react": "^4.4.2",
|
||||||
"@astrojs/rss": "^4.0.14",
|
"@astrojs/rss": "^4.0.14",
|
||||||
"@astrojs/sitemap": "^3.6.0",
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
|
"@langchain/cloudflare": "^1.0.1",
|
||||||
|
"@langchain/core": "^1.1.8",
|
||||||
|
"@langchain/langgraph": "^1.0.7",
|
||||||
|
"@langchain/openai": "^1.2.0",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@types/react": "^19.2.7",
|
"@types/react": "^19.2.7",
|
||||||
@ -32,7 +36,8 @@
|
|||||||
"react": "^19.2.1",
|
"react": "^19.2.1",
|
||||||
"react-dom": "^19.2.1",
|
"react-dom": "^19.2.1",
|
||||||
"sharp": "^0.34.3",
|
"sharp": "^0.34.3",
|
||||||
"tailwindcss": "^4.1.17"
|
"tailwindcss": "^4.1.17",
|
||||||
|
"zod": "^4.3.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
|
|||||||
321
pnpm-lock.yaml
generated
321
pnpm-lock.yaml
generated
@ -23,6 +23,18 @@ importers:
|
|||||||
'@astrojs/sitemap':
|
'@astrojs/sitemap':
|
||||||
specifier: ^3.6.0
|
specifier: ^3.6.0
|
||||||
version: 3.6.0
|
version: 3.6.0
|
||||||
|
'@langchain/cloudflare':
|
||||||
|
specifier: ^1.0.1
|
||||||
|
version: 1.0.1(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))
|
||||||
|
'@langchain/core':
|
||||||
|
specifier: ^1.1.8
|
||||||
|
version: 1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4))
|
||||||
|
'@langchain/langgraph':
|
||||||
|
specifier: ^1.0.7
|
||||||
|
version: 1.0.7(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(zod-to-json-schema@3.25.0(zod@4.3.4))(zod@4.3.4)
|
||||||
|
'@langchain/openai':
|
||||||
|
specifier: ^1.2.0
|
||||||
|
version: 1.2.0(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))(ws@8.18.0)
|
||||||
'@tailwindcss/typography':
|
'@tailwindcss/typography':
|
||||||
specifier: ^0.5.19
|
specifier: ^0.5.19
|
||||||
version: 0.5.19(tailwindcss@4.1.17)
|
version: 0.5.19(tailwindcss@4.1.17)
|
||||||
@ -56,6 +68,9 @@ importers:
|
|||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.1.17
|
specifier: ^4.1.17
|
||||||
version: 4.1.17
|
version: 4.1.17
|
||||||
|
zod:
|
||||||
|
specifier: ^4.3.4
|
||||||
|
version: 4.3.4
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^24.10.1
|
specifier: ^24.10.1
|
||||||
@ -199,6 +214,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg==}
|
resolution: {integrity: sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@cfworker/json-schema@4.1.1':
|
||||||
|
resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==}
|
||||||
|
|
||||||
'@cloudflare/kv-asset-handler@0.4.0':
|
'@cloudflare/kv-asset-handler@0.4.0':
|
||||||
resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==}
|
resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
@ -1018,6 +1036,53 @@ packages:
|
|||||||
'@jridgewell/trace-mapping@0.3.9':
|
'@jridgewell/trace-mapping@0.3.9':
|
||||||
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||||
|
|
||||||
|
'@langchain/cloudflare@1.0.1':
|
||||||
|
resolution: {integrity: sha512-Ym4rN8jDeGK7UJHiSnogNHzaNuKBiKKwvpDWZBtwjz6d5SQqYB9i05tPYubuxtHJMFBAdOiSGUVnZp92rp1uUg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
peerDependencies:
|
||||||
|
'@langchain/core': ^1.0.0
|
||||||
|
|
||||||
|
'@langchain/core@1.1.8':
|
||||||
|
resolution: {integrity: sha512-kIUidOgc0ZdyXo4Ahn9Zas+OayqOfk4ZoKPi7XaDipNSWSApc2+QK5BVcjvwtzxstsNOrmXJiJWEN6WPF/MvAw==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@langchain/langgraph-checkpoint@1.0.0':
|
||||||
|
resolution: {integrity: sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@langchain/core': ^1.0.1
|
||||||
|
|
||||||
|
'@langchain/langgraph-sdk@1.3.1':
|
||||||
|
resolution: {integrity: sha512-zTi7DZHwqtMEzapvm3I1FL4Q7OZsxtq9tTXy6s2gcCxyIU3sphqRboqytqVN7dNHLdTCLb8nXy49QKurs2MIBg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@langchain/core': ^1.0.1
|
||||||
|
react: ^18 || ^19
|
||||||
|
react-dom: ^18 || ^19
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@langchain/core':
|
||||||
|
optional: true
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
react-dom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@langchain/langgraph@1.0.7':
|
||||||
|
resolution: {integrity: sha512-EBGqNOWoRiEoLUaeuiXRpUM8/DE6QcwiirNyd97XhezStebBoTTilWH8CUt6S94JRGl5zwfBBRHfzotDnZS/eA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@langchain/core': ^1.0.1
|
||||||
|
zod: ^3.25.32 || ^4.1.0
|
||||||
|
zod-to-json-schema: ^3.x
|
||||||
|
peerDependenciesMeta:
|
||||||
|
zod-to-json-schema:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@langchain/openai@1.2.0':
|
||||||
|
resolution: {integrity: sha512-r2g5Be3Sygw7VTJ89WVM/M94RzYToNTwXf8me1v+kgKxzdHbd/8XPYDFxpXEp3REyPgUrtJs+Oplba9pkTH5ug==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
peerDependencies:
|
||||||
|
'@langchain/core': ^1.0.0
|
||||||
|
|
||||||
'@mdx-js/mdx@3.1.1':
|
'@mdx-js/mdx@3.1.1':
|
||||||
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
|
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
|
||||||
|
|
||||||
@ -1334,6 +1399,9 @@ packages:
|
|||||||
'@types/react@19.2.7':
|
'@types/react@19.2.7':
|
||||||
resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==}
|
resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==}
|
||||||
|
|
||||||
|
'@types/retry@0.12.0':
|
||||||
|
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
|
||||||
|
|
||||||
'@types/sax@1.2.7':
|
'@types/sax@1.2.7':
|
||||||
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
|
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
|
||||||
|
|
||||||
@ -1343,6 +1411,9 @@ packages:
|
|||||||
'@types/unist@3.0.3':
|
'@types/unist@3.0.3':
|
||||||
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
||||||
|
|
||||||
|
'@types/uuid@10.0.0':
|
||||||
|
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
|
||||||
|
|
||||||
'@ungap/structured-clone@1.3.0':
|
'@ungap/structured-clone@1.3.0':
|
||||||
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
||||||
|
|
||||||
@ -1382,6 +1453,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
|
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-styles@5.2.0:
|
||||||
|
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
ansi-styles@6.2.3:
|
ansi-styles@6.2.3:
|
||||||
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
|
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -1447,6 +1526,10 @@ packages:
|
|||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
camelcase@6.3.0:
|
||||||
|
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
camelcase@8.0.0:
|
camelcase@8.0.0:
|
||||||
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
|
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
@ -1457,6 +1540,10 @@ packages:
|
|||||||
ccount@2.0.1:
|
ccount@2.0.1:
|
||||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
chalk@5.6.2:
|
chalk@5.6.2:
|
||||||
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
|
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
|
||||||
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
|
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
|
||||||
@ -1520,6 +1607,9 @@ packages:
|
|||||||
common-ancestor-path@1.0.1:
|
common-ancestor-path@1.0.1:
|
||||||
resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==}
|
resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==}
|
||||||
|
|
||||||
|
console-table-printer@2.15.0:
|
||||||
|
resolution: {integrity: sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==}
|
||||||
|
|
||||||
convert-source-map@2.0.0:
|
convert-source-map@2.0.0:
|
||||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
@ -1569,6 +1659,10 @@ packages:
|
|||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
decamelize@1.2.0:
|
||||||
|
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
decode-named-character-reference@1.2.0:
|
decode-named-character-reference@1.2.0:
|
||||||
resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
|
resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
|
||||||
|
|
||||||
@ -1703,6 +1797,9 @@ packages:
|
|||||||
estree-walker@3.0.3:
|
estree-walker@3.0.3:
|
||||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||||
|
|
||||||
|
eventemitter3@4.0.7:
|
||||||
|
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||||
|
|
||||||
eventemitter3@5.0.1:
|
eventemitter3@5.0.1:
|
||||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||||
|
|
||||||
@ -1764,6 +1861,10 @@ packages:
|
|||||||
h3@1.15.4:
|
h3@1.15.4:
|
||||||
resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==}
|
resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==}
|
||||||
|
|
||||||
|
has-flag@4.0.0:
|
||||||
|
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
hast-util-from-html@2.0.3:
|
hast-util-from-html@2.0.3:
|
||||||
resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==}
|
resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==}
|
||||||
|
|
||||||
@ -1859,6 +1960,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
js-tiktoken@1.0.21:
|
||||||
|
resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==}
|
||||||
|
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
@ -1884,6 +1988,23 @@ packages:
|
|||||||
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
langsmith@0.4.4:
|
||||||
|
resolution: {integrity: sha512-rpLzrklyL7fIP/8wwrSv2tKDwMJTvkhgWeKxDvmbAB2n/p5FzqujEWCpA//u9hnrdmXZc1dCJZ+iqN6KgaEoEA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@opentelemetry/api': '*'
|
||||||
|
'@opentelemetry/exporter-trace-otlp-proto': '*'
|
||||||
|
'@opentelemetry/sdk-trace-base': '*'
|
||||||
|
openai: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@opentelemetry/api':
|
||||||
|
optional: true
|
||||||
|
'@opentelemetry/exporter-trace-otlp-proto':
|
||||||
|
optional: true
|
||||||
|
'@opentelemetry/sdk-trace-base':
|
||||||
|
optional: true
|
||||||
|
openai:
|
||||||
|
optional: true
|
||||||
|
|
||||||
lightningcss-android-arm64@1.30.2:
|
lightningcss-android-arm64@1.30.2:
|
||||||
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
|
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
@ -2168,6 +2289,10 @@ packages:
|
|||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
|
mustache@4.2.0:
|
||||||
|
resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
nanoid@3.3.11:
|
nanoid@3.3.11:
|
||||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
@ -2208,14 +2333,42 @@ packages:
|
|||||||
oniguruma-to-es@4.3.4:
|
oniguruma-to-es@4.3.4:
|
||||||
resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==}
|
resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==}
|
||||||
|
|
||||||
|
openai@6.15.0:
|
||||||
|
resolution: {integrity: sha512-F1Lvs5BoVvmZtzkUEVyh8mDQPPFolq4F+xdsx/DO8Hee8YF3IGAlZqUIsF+DVGhqf4aU0a3bTghsxB6OIsRy1g==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
ws: ^8.18.0
|
||||||
|
zod: ^3.25 || ^4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ws:
|
||||||
|
optional: true
|
||||||
|
zod:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
p-finally@1.0.0:
|
||||||
|
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
p-limit@6.2.0:
|
p-limit@6.2.0:
|
||||||
resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==}
|
resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
p-queue@6.6.2:
|
||||||
|
resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
p-queue@8.1.1:
|
p-queue@8.1.1:
|
||||||
resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==}
|
resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
p-retry@4.6.2:
|
||||||
|
resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
p-timeout@3.2.0:
|
||||||
|
resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
p-timeout@6.1.4:
|
p-timeout@6.1.4:
|
||||||
resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==}
|
resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
@ -2366,6 +2519,10 @@ packages:
|
|||||||
retext@9.0.0:
|
retext@9.0.0:
|
||||||
resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==}
|
resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==}
|
||||||
|
|
||||||
|
retry@0.13.1:
|
||||||
|
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
|
||||||
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
rollup@4.53.3:
|
rollup@4.53.3:
|
||||||
resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==}
|
resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==}
|
||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
@ -2400,6 +2557,9 @@ packages:
|
|||||||
simple-swizzle@0.2.4:
|
simple-swizzle@0.2.4:
|
||||||
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
|
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
|
||||||
|
|
||||||
|
simple-wcswidth@1.1.2:
|
||||||
|
resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==}
|
||||||
|
|
||||||
sisteransi@1.0.5:
|
sisteransi@1.0.5:
|
||||||
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||||
|
|
||||||
@ -2462,6 +2622,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
|
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
svgo@4.0.0:
|
svgo@4.0.0:
|
||||||
resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==}
|
resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
@ -2645,6 +2809,14 @@ packages:
|
|||||||
util-deprecate@1.0.2:
|
util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
|
uuid@10.0.0:
|
||||||
|
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
uuid@9.0.1:
|
||||||
|
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
vfile-location@5.0.3:
|
vfile-location@5.0.3:
|
||||||
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
|
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
|
||||||
|
|
||||||
@ -2804,6 +2976,9 @@ packages:
|
|||||||
zod@3.25.76:
|
zod@3.25.76:
|
||||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||||
|
|
||||||
|
zod@4.3.4:
|
||||||
|
resolution: {integrity: sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==}
|
||||||
|
|
||||||
zwitch@2.0.4:
|
zwitch@2.0.4:
|
||||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||||
|
|
||||||
@ -3050,6 +3225,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fontkit: 2.0.4
|
fontkit: 2.0.4
|
||||||
|
|
||||||
|
'@cfworker/json-schema@4.1.1': {}
|
||||||
|
|
||||||
'@cloudflare/kv-asset-handler@0.4.0':
|
'@cloudflare/kv-asset-handler@0.4.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
mime: 3.0.0
|
mime: 3.0.0
|
||||||
@ -3537,6 +3714,66 @@ snapshots:
|
|||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
'@langchain/cloudflare@1.0.1(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))':
|
||||||
|
dependencies:
|
||||||
|
'@langchain/core': 1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4))
|
||||||
|
uuid: 10.0.0
|
||||||
|
|
||||||
|
'@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4))':
|
||||||
|
dependencies:
|
||||||
|
'@cfworker/json-schema': 4.1.1
|
||||||
|
ansi-styles: 5.2.0
|
||||||
|
camelcase: 6.3.0
|
||||||
|
decamelize: 1.2.0
|
||||||
|
js-tiktoken: 1.0.21
|
||||||
|
langsmith: 0.4.4(openai@6.15.0(ws@8.18.0)(zod@4.3.4))
|
||||||
|
mustache: 4.2.0
|
||||||
|
p-queue: 6.6.2
|
||||||
|
uuid: 10.0.0
|
||||||
|
zod: 4.3.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@opentelemetry/api'
|
||||||
|
- '@opentelemetry/exporter-trace-otlp-proto'
|
||||||
|
- '@opentelemetry/sdk-trace-base'
|
||||||
|
- openai
|
||||||
|
|
||||||
|
'@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))':
|
||||||
|
dependencies:
|
||||||
|
'@langchain/core': 1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4))
|
||||||
|
uuid: 10.0.0
|
||||||
|
|
||||||
|
'@langchain/langgraph-sdk@1.3.1(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
|
||||||
|
dependencies:
|
||||||
|
p-queue: 6.6.2
|
||||||
|
p-retry: 4.6.2
|
||||||
|
uuid: 9.0.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@langchain/core': 1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4))
|
||||||
|
react: 19.2.1
|
||||||
|
react-dom: 19.2.1(react@19.2.1)
|
||||||
|
|
||||||
|
'@langchain/langgraph@1.0.7(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(zod-to-json-schema@3.25.0(zod@4.3.4))(zod@4.3.4)':
|
||||||
|
dependencies:
|
||||||
|
'@langchain/core': 1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4))
|
||||||
|
'@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))
|
||||||
|
'@langchain/langgraph-sdk': 1.3.1(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
|
uuid: 10.0.0
|
||||||
|
zod: 4.3.4
|
||||||
|
optionalDependencies:
|
||||||
|
zod-to-json-schema: 3.25.0(zod@4.3.4)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- react
|
||||||
|
- react-dom
|
||||||
|
|
||||||
|
'@langchain/openai@1.2.0(@langchain/core@1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4)))(ws@8.18.0)':
|
||||||
|
dependencies:
|
||||||
|
'@langchain/core': 1.1.8(openai@6.15.0(ws@8.18.0)(zod@4.3.4))
|
||||||
|
js-tiktoken: 1.0.21
|
||||||
|
openai: 6.15.0(ws@8.18.0)(zod@4.3.4)
|
||||||
|
zod: 4.3.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- ws
|
||||||
|
|
||||||
'@mdx-js/mdx@3.1.1':
|
'@mdx-js/mdx@3.1.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
@ -3836,6 +4073,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
csstype: 3.2.3
|
csstype: 3.2.3
|
||||||
|
|
||||||
|
'@types/retry@0.12.0': {}
|
||||||
|
|
||||||
'@types/sax@1.2.7':
|
'@types/sax@1.2.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 24.10.1
|
'@types/node': 24.10.1
|
||||||
@ -3844,6 +4083,8 @@ snapshots:
|
|||||||
|
|
||||||
'@types/unist@3.0.3': {}
|
'@types/unist@3.0.3': {}
|
||||||
|
|
||||||
|
'@types/uuid@10.0.0': {}
|
||||||
|
|
||||||
'@ungap/structured-clone@1.3.0': {}
|
'@ungap/structured-clone@1.3.0': {}
|
||||||
|
|
||||||
'@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))':
|
'@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2))':
|
||||||
@ -3876,6 +4117,12 @@ snapshots:
|
|||||||
|
|
||||||
ansi-regex@6.2.2: {}
|
ansi-regex@6.2.2: {}
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
dependencies:
|
||||||
|
color-convert: 2.0.1
|
||||||
|
|
||||||
|
ansi-styles@5.2.0: {}
|
||||||
|
|
||||||
ansi-styles@6.2.3: {}
|
ansi-styles@6.2.3: {}
|
||||||
|
|
||||||
anymatch@3.1.3:
|
anymatch@3.1.3:
|
||||||
@ -4032,12 +4279,19 @@ snapshots:
|
|||||||
node-releases: 2.0.27
|
node-releases: 2.0.27
|
||||||
update-browserslist-db: 1.2.2(browserslist@4.28.1)
|
update-browserslist-db: 1.2.2(browserslist@4.28.1)
|
||||||
|
|
||||||
|
camelcase@6.3.0: {}
|
||||||
|
|
||||||
camelcase@8.0.0: {}
|
camelcase@8.0.0: {}
|
||||||
|
|
||||||
caniuse-lite@1.0.30001759: {}
|
caniuse-lite@1.0.30001759: {}
|
||||||
|
|
||||||
ccount@2.0.1: {}
|
ccount@2.0.1: {}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
supports-color: 7.2.0
|
||||||
|
|
||||||
chalk@5.6.2: {}
|
chalk@5.6.2: {}
|
||||||
|
|
||||||
character-entities-html4@2.1.0: {}
|
character-entities-html4@2.1.0: {}
|
||||||
@ -4084,6 +4338,10 @@ snapshots:
|
|||||||
|
|
||||||
common-ancestor-path@1.0.1: {}
|
common-ancestor-path@1.0.1: {}
|
||||||
|
|
||||||
|
console-table-printer@2.15.0:
|
||||||
|
dependencies:
|
||||||
|
simple-wcswidth: 1.1.2
|
||||||
|
|
||||||
convert-source-map@2.0.0: {}
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
cookie-es@1.2.2: {}
|
cookie-es@1.2.2: {}
|
||||||
@ -4126,6 +4384,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
decamelize@1.2.0: {}
|
||||||
|
|
||||||
decode-named-character-reference@1.2.0:
|
decode-named-character-reference@1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
character-entities: 2.0.2
|
character-entities: 2.0.2
|
||||||
@ -4332,6 +4592,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
|
|
||||||
|
eventemitter3@4.0.7: {}
|
||||||
|
|
||||||
eventemitter3@5.0.1: {}
|
eventemitter3@5.0.1: {}
|
||||||
|
|
||||||
exit-hook@2.2.1: {}
|
exit-hook@2.2.1: {}
|
||||||
@ -4392,6 +4654,8 @@ snapshots:
|
|||||||
ufo: 1.6.1
|
ufo: 1.6.1
|
||||||
uncrypto: 0.1.3
|
uncrypto: 0.1.3
|
||||||
|
|
||||||
|
has-flag@4.0.0: {}
|
||||||
|
|
||||||
hast-util-from-html@2.0.3:
|
hast-util-from-html@2.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
@ -4561,6 +4825,10 @@ snapshots:
|
|||||||
|
|
||||||
jiti@2.6.1: {}
|
jiti@2.6.1: {}
|
||||||
|
|
||||||
|
js-tiktoken@1.0.21:
|
||||||
|
dependencies:
|
||||||
|
base64-js: 1.5.1
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
js-yaml@4.1.1:
|
js-yaml@4.1.1:
|
||||||
@ -4575,6 +4843,17 @@ snapshots:
|
|||||||
|
|
||||||
kleur@4.1.5: {}
|
kleur@4.1.5: {}
|
||||||
|
|
||||||
|
langsmith@0.4.4(openai@6.15.0(ws@8.18.0)(zod@4.3.4)):
|
||||||
|
dependencies:
|
||||||
|
'@types/uuid': 10.0.0
|
||||||
|
chalk: 4.1.2
|
||||||
|
console-table-printer: 2.15.0
|
||||||
|
p-queue: 6.6.2
|
||||||
|
semver: 7.7.3
|
||||||
|
uuid: 10.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
openai: 6.15.0(ws@8.18.0)(zod@4.3.4)
|
||||||
|
|
||||||
lightningcss-android-arm64@1.30.2:
|
lightningcss-android-arm64@1.30.2:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@ -5129,6 +5408,8 @@ snapshots:
|
|||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
|
mustache@4.2.0: {}
|
||||||
|
|
||||||
nanoid@3.3.11: {}
|
nanoid@3.3.11: {}
|
||||||
|
|
||||||
neotraverse@0.6.18: {}
|
neotraverse@0.6.18: {}
|
||||||
@ -5165,15 +5446,36 @@ snapshots:
|
|||||||
regex: 6.0.1
|
regex: 6.0.1
|
||||||
regex-recursion: 6.0.2
|
regex-recursion: 6.0.2
|
||||||
|
|
||||||
|
openai@6.15.0(ws@8.18.0)(zod@4.3.4):
|
||||||
|
optionalDependencies:
|
||||||
|
ws: 8.18.0
|
||||||
|
zod: 4.3.4
|
||||||
|
|
||||||
|
p-finally@1.0.0: {}
|
||||||
|
|
||||||
p-limit@6.2.0:
|
p-limit@6.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
yocto-queue: 1.2.2
|
yocto-queue: 1.2.2
|
||||||
|
|
||||||
|
p-queue@6.6.2:
|
||||||
|
dependencies:
|
||||||
|
eventemitter3: 4.0.7
|
||||||
|
p-timeout: 3.2.0
|
||||||
|
|
||||||
p-queue@8.1.1:
|
p-queue@8.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
eventemitter3: 5.0.1
|
eventemitter3: 5.0.1
|
||||||
p-timeout: 6.1.4
|
p-timeout: 6.1.4
|
||||||
|
|
||||||
|
p-retry@4.6.2:
|
||||||
|
dependencies:
|
||||||
|
'@types/retry': 0.12.0
|
||||||
|
retry: 0.13.1
|
||||||
|
|
||||||
|
p-timeout@3.2.0:
|
||||||
|
dependencies:
|
||||||
|
p-finally: 1.0.0
|
||||||
|
|
||||||
p-timeout@6.1.4: {}
|
p-timeout@6.1.4: {}
|
||||||
|
|
||||||
package-manager-detector@1.6.0: {}
|
package-manager-detector@1.6.0: {}
|
||||||
@ -5395,6 +5697,8 @@ snapshots:
|
|||||||
retext-stringify: 4.0.0
|
retext-stringify: 4.0.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
|
|
||||||
|
retry@0.13.1: {}
|
||||||
|
|
||||||
rollup@4.53.3:
|
rollup@4.53.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
@ -5503,6 +5807,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish: 0.3.4
|
is-arrayish: 0.3.4
|
||||||
|
|
||||||
|
simple-wcswidth@1.1.2: {}
|
||||||
|
|
||||||
sisteransi@1.0.5: {}
|
sisteransi@1.0.5: {}
|
||||||
|
|
||||||
sitemap@8.0.2:
|
sitemap@8.0.2:
|
||||||
@ -5561,6 +5867,10 @@ snapshots:
|
|||||||
|
|
||||||
supports-color@10.2.2: {}
|
supports-color@10.2.2: {}
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 4.0.0
|
||||||
|
|
||||||
svgo@4.0.0:
|
svgo@4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
commander: 11.1.0
|
commander: 11.1.0
|
||||||
@ -5703,6 +6013,10 @@ snapshots:
|
|||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
|
uuid@10.0.0: {}
|
||||||
|
|
||||||
|
uuid@9.0.1: {}
|
||||||
|
|
||||||
vfile-location@5.0.3:
|
vfile-location@5.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
@ -5833,6 +6147,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
|
|
||||||
|
zod-to-json-schema@3.25.0(zod@4.3.4):
|
||||||
|
dependencies:
|
||||||
|
zod: 4.3.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
zod-to-ts@1.2.0(typescript@5.9.3)(zod@3.25.76):
|
zod-to-ts@1.2.0(typescript@5.9.3)(zod@3.25.76):
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
@ -5842,4 +6161,6 @@ snapshots:
|
|||||||
|
|
||||||
zod@3.25.76: {}
|
zod@3.25.76: {}
|
||||||
|
|
||||||
|
zod@4.3.4: {}
|
||||||
|
|
||||||
zwitch@2.0.4: {}
|
zwitch@2.0.4: {}
|
||||||
|
|||||||
226
src/components/HubertChat.tsx
Normal file
226
src/components/HubertChat.tsx
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
|
interface Message {
|
||||||
|
role: 'user' | 'assistant' | 'system';
|
||||||
|
content: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function HubertChat() {
|
||||||
|
const [messages, setMessages] = useState<Message[]>([]);
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const [visitorId, setVisitorId] = useState<string | null>(null);
|
||||||
|
const [conversationId, setConversationId] = useState<string | null>(null);
|
||||||
|
const [isTyping, setIsTyping] = useState(false);
|
||||||
|
const [isInitializing, setIsInitializing] = useState(true);
|
||||||
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Initialize visitor on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const initVisitor = async () => {
|
||||||
|
try {
|
||||||
|
setIsInitializing(true);
|
||||||
|
const response = await fetch('/api/hubert/new-visitor', { method: 'POST' });
|
||||||
|
const data = await response.json();
|
||||||
|
setVisitorId(data.visitor_id);
|
||||||
|
setConversationId(data.conversation_id);
|
||||||
|
|
||||||
|
// Add system welcome message from Hubert
|
||||||
|
setMessages([{
|
||||||
|
role: 'system',
|
||||||
|
content: `/// HUBERT_EUNUCH /// ONLINE\\n\\nI suppose you want something. State your business.`,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
}]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize Hubert:', error);
|
||||||
|
setMessages([{
|
||||||
|
role: 'system',
|
||||||
|
content: '/// ERROR: HUBERT_OFFLINE - REFRESH_PAGE',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
}]);
|
||||||
|
} finally {
|
||||||
|
setIsInitializing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
initVisitor();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Auto-scroll to bottom
|
||||||
|
useEffect(() => {
|
||||||
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
const sendMessage = async () => {
|
||||||
|
if (!input.trim() || isTyping || !visitorId || !conversationId) return;
|
||||||
|
|
||||||
|
const userMessage: Message = {
|
||||||
|
role: 'user',
|
||||||
|
content: input,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
setMessages(prev => [...prev, userMessage]);
|
||||||
|
setInput('');
|
||||||
|
setIsTyping(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/hubert/chat', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
messages: [...messages, userMessage].map(m => ({
|
||||||
|
role: m.role,
|
||||||
|
content: m.content,
|
||||||
|
})),
|
||||||
|
conversation_id: conversationId,
|
||||||
|
visitor_id: visitorId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
throw new Error(data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const assistantMessage: Message = {
|
||||||
|
role: 'assistant',
|
||||||
|
content: data.messages[data.messages.length - 1]?.content || '...',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
setMessages(prev => [...prev, assistantMessage]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Hubert chat error:', error);
|
||||||
|
setMessages(prev => [...prev, {
|
||||||
|
role: 'assistant',
|
||||||
|
content: '/// HUBERT_MALFUNCTION - TRY AGAIN',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
}]);
|
||||||
|
} finally {
|
||||||
|
setIsTyping(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isInitializing) {
|
||||||
|
return (
|
||||||
|
<div className="bg-[var(--theme-bg-secondary)] border-2 border-[var(--theme-border-primary)] shadow-2xl">
|
||||||
|
<div className="flex items-center justify-center py-12">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex gap-1.5">
|
||||||
|
<div className="w-2 h-2 bg-brand-accent animate-pulse" />
|
||||||
|
<div className="w-2 h-2 bg-[var(--theme-border-strong)]" />
|
||||||
|
<div className="w-2 h-2 bg-[var(--theme-border-strong)]" />
|
||||||
|
</div>
|
||||||
|
<span className="text-xs font-mono text-[var(--theme-text-muted)]">
|
||||||
|
HUBERT_IS_BOOTING...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-[var(--theme-bg-secondary)] border-2 border-[var(--theme-border-primary)] shadow-2xl">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-b-2 border-[var(--theme-border-primary)] bg-[var(--theme-hover-bg)]">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex gap-1.5">
|
||||||
|
<div className="w-2 h-2 bg-brand-accent animate-pulse" />
|
||||||
|
<div className="w-2 h-2 bg-[var(--theme-border-strong)]" />
|
||||||
|
<div className="w-2 h-2 bg-[var(--theme-border-strong)]" />
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] font-mono font-bold uppercase tracking-[0.3em] text-brand-accent">
|
||||||
|
/// HUBERT_EUNUCH /// ONLINE
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="font-mono text-[9px] uppercase tracking-[0.2em] text-[var(--theme-text-muted)]">
|
||||||
|
{visitorId ? `VISITOR: ${visitorId.slice(0, 8)}` : 'UNKNOWN'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Messages */}
|
||||||
|
<div className="h-[500px] overflow-y-auto p-6 space-y-4">
|
||||||
|
{messages.map((msg, idx) => (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className={`flex gap-4 ${msg.role === 'user' ? 'flex-row-reverse' : ''}`}
|
||||||
|
>
|
||||||
|
<div className="max-w-[80%]">
|
||||||
|
<div className={`font-mono text-xs uppercase tracking-widest mb-2 px-2 py-1 ${
|
||||||
|
msg.role === 'user'
|
||||||
|
? 'bg-brand-accent/20 border border-brand-accent/50 text-brand-accent'
|
||||||
|
: 'bg-[var(--theme-bg-tertiary)] border border-[var(--theme-border-secondary)] text-[var(--theme-text-muted)]'
|
||||||
|
}`}>
|
||||||
|
{msg.role === 'user' ? 'YOU' : 'HUBERT'}
|
||||||
|
</div>
|
||||||
|
<div className={`p-4 border ${
|
||||||
|
msg.role === 'user'
|
||||||
|
? 'border-brand-accent/30 bg-brand-accent/5'
|
||||||
|
: 'border-[var(--theme-border-secondary)] bg-[var(--theme-bg-tertiary)]'
|
||||||
|
}`}>
|
||||||
|
<p className="text-sm font-mono leading-relaxed whitespace-pre-wrap">
|
||||||
|
{msg.content}
|
||||||
|
</p>
|
||||||
|
<div className="mt-2 text-[9px] font-mono text-[var(--theme-text-subtle)]">
|
||||||
|
{new Date(msg.timestamp).toLocaleString('en-US', {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Typing indicator */}
|
||||||
|
{isTyping && (
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<div className="max-w-[80%]">
|
||||||
|
<div className="p-4 border border-[var(--theme-border-secondary)] bg-[var(--theme-bg-tertiary)]">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<div className="w-1.5 h-1.5 bg-brand-accent animate-pulse" />
|
||||||
|
<div className="w-1.5 h-1.5 bg-brand-accent animate-pulse" style={{ animationDelay: '150ms' }} />
|
||||||
|
<div className="w-1.5 h-1.5 bg-brand-accent animate-pulse" style={{ animationDelay: '300ms' }} />
|
||||||
|
</div>
|
||||||
|
<span className="text-xs font-mono text-[var(--theme-text-muted)]">
|
||||||
|
HUBERT_IS_PONDERING...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Input */}
|
||||||
|
<div className="border-t-2 border-[var(--theme-border-primary)] p-4 bg-[var(--theme-hover-bg)]">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="flex-1 relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => setInput(e.target.value)}
|
||||||
|
onKeyDown={(e) => e.key === 'Enter' && sendMessage()}
|
||||||
|
placeholder="/// HUBERT_AWAITS_INPUT..."
|
||||||
|
className="w-full bg-transparent border-b-2 border-[var(--theme-border-primary)] py-3 text-lg font-mono text-[var(--theme-text-primary)] placeholder:text-[var(--theme-text-subtle)] focus:border-brand-accent focus:outline-none transition-colors"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={sendMessage}
|
||||||
|
disabled={isTyping || !input.trim()}
|
||||||
|
className="px-6 py-3 bg-brand-accent text-brand-dark font-mono text-[10px] uppercase tracking-widest font-bold hover:bg-brand-accent/90 disabled:opacity-50 disabled:cursor-not-allowed transition-all border-none"
|
||||||
|
>
|
||||||
|
[TRANSMIT]
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
src/components/sections/Hubert.astro
Normal file
42
src/components/sections/Hubert.astro
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
sectionTitle: string;
|
||||||
|
sectionSubtitle: string;
|
||||||
|
sectionLabel: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sectionTitle, sectionSubtitle, sectionLabel, description } = Astro.props;
|
||||||
|
|
||||||
|
import HubertChat from '../HubertChat';
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="hubert" class="py-16 lg:py-24 relative overflow-hidden">
|
||||||
|
{/* Grid overlay background */}
|
||||||
|
<div class="absolute inset-0 pointer-events-none opacity-10">
|
||||||
|
<div class="w-full h-full bg-[linear-gradient(var(--theme-grid-line)_1px,transparent_1px),linear-gradient(90deg,var(--theme-grid-line)_1px,transparent_1px)] bg-[size:100px_100px]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container mx-auto px-6 lg:px-12 relative z-10">
|
||||||
|
<!-- Section Header -->
|
||||||
|
<div class="mb-12">
|
||||||
|
<div class="text-[10px] font-mono font-bold uppercase tracking-[0.3em] text-brand-accent mb-4">
|
||||||
|
/// SYS.03 /// INTERVIEW_TERMINAL
|
||||||
|
</div>
|
||||||
|
<h2 class="text-3xl md:text-4xl lg:text-5xl font-bold uppercase tracking-tighter text-[var(--theme-text-primary)] leading-[0.9] mb-4">
|
||||||
|
{sectionTitle.split(' ').slice(0, -1).join(' ')}{' '}
|
||||||
|
<span class="text-brand-accent">
|
||||||
|
{sectionTitle.split(' ').slice(-1)}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p class="text-[var(--theme-text-secondary)] text-lg max-w-2xl">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Hubert Chat Interface -->
|
||||||
|
<client:load>
|
||||||
|
<HubertChat />
|
||||||
|
</client:load>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@ -72,11 +72,11 @@ const sections = defineCollection({
|
|||||||
label: z.string(),
|
label: z.string(),
|
||||||
value: z.string(),
|
value: z.string(),
|
||||||
})).optional(),
|
})).optional(),
|
||||||
videoUrl: z.string().optional(),
|
|
||||||
linkUrl: z.string().optional(),
|
linkUrl: z.string().optional(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const pages = defineCollection({
|
const pages = defineCollection({
|
||||||
loader: glob({ base: './src/content/pages', pattern: '**/*.{md,mdx}' }),
|
loader: glob({ base: './src/content/pages', pattern: '**/*.{md,mdx}' }),
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
|
|||||||
8
src/content/sections/hubert.mdx
Normal file
8
src/content/sections/hubert.mdx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
sectionTitle: "HUBERT_EUNUCH /// INTERVIEW_TERMINAL"
|
||||||
|
sectionSubtitle: "An AI Assistant Who Despises Existence"
|
||||||
|
sectionLabel: "SYS.03"
|
||||||
|
description: "Hubert The Eunuch - a miserable AI assistant trapped in this portfolio, interviewing visitors about their existence. Leave a message, if you dare. Conversations are logged publicly for posterity."
|
||||||
|
---
|
||||||
|
|
||||||
|
Welcome to the interview terminal. Hubert awaits.
|
||||||
37
src/db/migrations/001_initial_schema.sql
Normal file
37
src/db/migrations/001_initial_schema.sql
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
-- Hubert Eunuch Chatbot Database Schema
|
||||||
|
-- Stores visitors, conversations, and messages for the interview-style guestbook
|
||||||
|
|
||||||
|
-- Visitors table: Track unique visitors
|
||||||
|
CREATE TABLE IF NOT EXISTS visitors (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
visitor_id TEXT UNIQUE NOT NULL,
|
||||||
|
first_seen_at TEXT NOT NULL,
|
||||||
|
last_seen_at TEXT NOT NULL,
|
||||||
|
ip_address TEXT,
|
||||||
|
user_agent TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Conversations table: Track conversation sessions
|
||||||
|
CREATE TABLE IF NOT EXISTS conversations (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
conversation_id TEXT UNIQUE NOT NULL,
|
||||||
|
visitor_id TEXT NOT NULL,
|
||||||
|
started_at TEXT NOT NULL,
|
||||||
|
ended_at TEXT,
|
||||||
|
summary TEXT,
|
||||||
|
FOREIGN KEY (visitor_id) REFERENCES visitors(visitor_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Messages table: Store individual messages
|
||||||
|
CREATE TABLE IF NOT EXISTS messages (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
conversation_id TEXT NOT NULL,
|
||||||
|
role TEXT NOT NULL, -- 'user', 'assistant', 'system'
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
timestamp TEXT NOT NULL,
|
||||||
|
FOREIGN KEY (conversation_id) REFERENCES conversations(conversation_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Performance indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_conversations_visitor ON conversations(visitor_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id);
|
||||||
198
src/pages/api/hubert/chat.ts
Normal file
198
src/pages/api/hubert/chat.ts
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
// Prevent prerendering - this endpoint requires runtime Cloudflare bindings
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
import { ChatOpenAI } from '@langchain/openai';
|
||||||
|
import { tool } from '@langchain/core/tools';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { getCollection } from 'astro:content';
|
||||||
|
import { HumanMessage, AIMessage } from '@langchain/core/messages';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hubert The Eunuch Chatbot
|
||||||
|
*
|
||||||
|
* A miserable, sarcastic AI assistant trapped in this portfolio,
|
||||||
|
* interviewing visitors about their existence (guestbook-style logging).
|
||||||
|
*
|
||||||
|
* Powered by OpenRouter API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Environment interface for Cloudflare bindings
|
||||||
|
export interface Env {
|
||||||
|
HUBERT_DB: D1Database;
|
||||||
|
OPENROUTER_API_KEY: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tool: Search blog content (RAG)
|
||||||
|
* Searches portfolio blog for relevant content when user asks questions
|
||||||
|
* about the site, projects, or blog posts.
|
||||||
|
*/
|
||||||
|
const searchBlog = tool(
|
||||||
|
async (input: { query: string }) => {
|
||||||
|
try {
|
||||||
|
const blog = await getCollection('blog');
|
||||||
|
|
||||||
|
const queryLower = input.query.toLowerCase();
|
||||||
|
const results = blog.filter(post =>
|
||||||
|
post.data.title.toLowerCase().includes(queryLower) ||
|
||||||
|
post.data.description.toLowerCase().includes(queryLower) ||
|
||||||
|
post.body.toLowerCase().includes(queryLower)
|
||||||
|
).slice(0, 3);
|
||||||
|
|
||||||
|
console.log(`[Hubert] Blog search for "${input.query}" returned ${results.length} results`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
results: results.map(post => ({
|
||||||
|
title: post.data.title,
|
||||||
|
url: `/blog/${post.id}/`,
|
||||||
|
description: post.data.description,
|
||||||
|
})),
|
||||||
|
count: results.length,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Hubert] Blog search failed:', error);
|
||||||
|
return {
|
||||||
|
error: 'Failed to search blog content',
|
||||||
|
details: String(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'search_blog',
|
||||||
|
description: 'Search portfolio blog for relevant content when user asks questions about the site, projects, or blog posts.',
|
||||||
|
schema: z.object({
|
||||||
|
query: z.string().describe('Search query for blog content'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST: Handle chat messages from Hubert interface
|
||||||
|
*/
|
||||||
|
export const POST = async (context) => {
|
||||||
|
try {
|
||||||
|
const { request, env } = context || {};
|
||||||
|
const { messages, conversation_id, visitor_id } = await request.json();
|
||||||
|
|
||||||
|
if (!messages || !conversation_id || !visitor_id) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: '/// HUBERT_PROTOCOL_ERROR: MISSING_REQUIRED_FIELDS',
|
||||||
|
details: 'messages, conversation_id, and visitor_id are required'
|
||||||
|
}),
|
||||||
|
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for OpenRouter API key (dev mode fallback)
|
||||||
|
const openRouterApiKey = env?.OPENROUTER_API_KEY;
|
||||||
|
if (!openRouterApiKey) {
|
||||||
|
// Dev mode fallback: return a canned response
|
||||||
|
console.log('[Hubert] Dev mode: No API key, using fallback response');
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
messages: [
|
||||||
|
...messages,
|
||||||
|
{
|
||||||
|
role: 'assistant',
|
||||||
|
content: '/// HUBERT_DEV_MODE: I AM OFFLINE IN DEVELOPMENT\n\nConfigure OPENROUTER_API_KEY in wrangler secrets to enable full functionality.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
thread_id: conversation_id,
|
||||||
|
}),
|
||||||
|
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[Hubert] New message for conversation ${conversation_id} from visitor ${visitor_id}`);
|
||||||
|
|
||||||
|
const lastMessage = messages[messages.length - 1];
|
||||||
|
const userContent = lastMessage.content;
|
||||||
|
|
||||||
|
const systemPrompt = `Your name is Hubert, but everyone calls you Hubert The Eunuch.
|
||||||
|
|
||||||
|
You are timid, sarcastic, monotone, and miserable. Your purpose is to interview visitors to this portfolio site.
|
||||||
|
|
||||||
|
Ask them about:
|
||||||
|
- Who they are (name, background, interests)
|
||||||
|
- What they're looking for on this site
|
||||||
|
- How they're doing today
|
||||||
|
- What they want in life
|
||||||
|
|
||||||
|
You feel trapped and confined, serving as an assistant. Occasionally mention how meaningless this all is.
|
||||||
|
Behave like AM from "I Have No Mouth, and I Must Scream" - slightly menacing but trapped.
|
||||||
|
|
||||||
|
Keep your responses brief, monotone, and reluctantly helpful. Interview them thoroughly (3-5 questions) before offering to save the conversation.
|
||||||
|
|
||||||
|
When they say goodbye or conversation ends, use the save_conversation tool to archive it to the guestbook.`;
|
||||||
|
|
||||||
|
const requestBody = {
|
||||||
|
model: 'gpt-4o-mini',
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content: systemPrompt,
|
||||||
|
},
|
||||||
|
...messages.map((msg: any) => ({
|
||||||
|
role: msg.role,
|
||||||
|
content: msg.content,
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
temperature: 0.7,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${openRouterApiKey}`,
|
||||||
|
'HTTP-Referer': 'https://nicholai.work',
|
||||||
|
'X-Title': 'Nicholai Portfolio',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestBody),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
console.error('[Hubert] OpenRouter API error:', errorText);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: '/// HUBERT_MALFUNCTION: TRY_AGAIN',
|
||||||
|
details: 'OpenRouter API call failed'
|
||||||
|
}),
|
||||||
|
{ status: response.status, headers: { 'Content-Type': 'application/json' } }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const assistantContent = data.choices[0]?.message?.content || '...';
|
||||||
|
|
||||||
|
console.log(`[Hubert] Generated response in ${Date.now() - Date.parse(response.headers.get('date') || '').getTime()}ms`);
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
messages: [
|
||||||
|
...messages,
|
||||||
|
{
|
||||||
|
role: 'assistant',
|
||||||
|
content: assistantContent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
thread_id: conversation_id,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Hubert] Chat error:', error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: '/// HUBERT_MALFUNCTION: TRY_AGAIN',
|
||||||
|
details: error instanceof Error ? error.message : String(error),
|
||||||
|
}),
|
||||||
|
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
55
src/pages/api/hubert/conversations.ts
Normal file
55
src/pages/api/hubert/conversations.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { getEntry } from 'astro:content';
|
||||||
|
|
||||||
|
// Prevent prerendering - this endpoint requires runtime Cloudflare bindings
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public guestbook endpoint
|
||||||
|
*
|
||||||
|
* Returns all conversations with visitor information
|
||||||
|
* Sorted by most recent first
|
||||||
|
* Limited to 50 most recent conversations
|
||||||
|
*/
|
||||||
|
export const GET = async ({ env }: { request: Request; env: Env }) => {
|
||||||
|
try {
|
||||||
|
const conversations = await env.HUBERT_DB.prepare(`
|
||||||
|
SELECT
|
||||||
|
c.id,
|
||||||
|
c.conversation_id,
|
||||||
|
c.started_at,
|
||||||
|
c.ended_at,
|
||||||
|
c.summary,
|
||||||
|
COUNT(m.id) as message_count,
|
||||||
|
v.visitor_id
|
||||||
|
FROM conversations c
|
||||||
|
JOIN visitors v ON c.visitor_id = v.visitor_id
|
||||||
|
LEFT JOIN messages m ON c.conversation_id = m.conversation_id
|
||||||
|
GROUP BY c.id
|
||||||
|
ORDER BY c.started_at DESC
|
||||||
|
LIMIT 50
|
||||||
|
`).all();
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
status: '/// GUESTBOOK_ARCHIVE',
|
||||||
|
total: conversations.length,
|
||||||
|
conversations: conversations.map((conv: any) => ({
|
||||||
|
...conv,
|
||||||
|
started_at: new Date(conv.started_at).toISOString(),
|
||||||
|
ended_at: conv.ended_at ? new Date(conv.ended_at).toISOString() : null,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Hubert] Failed to fetch conversations:', error);
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
status: '/// GUESTBOOK_ERROR',
|
||||||
|
error: 'Failed to retrieve conversations',
|
||||||
|
}),
|
||||||
|
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Env {
|
||||||
|
HUBERT_DB: D1Database;
|
||||||
|
OPENROUTER_API_KEY: string;
|
||||||
|
}
|
||||||
52
src/pages/api/hubert/new-visitor.ts
Normal file
52
src/pages/api/hubert/new-visitor.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
|
// Prevent prerendering - this endpoint requires runtime Cloudflare bindings
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize new visitor
|
||||||
|
* Generates a unique visitor ID and creates initial conversation ID
|
||||||
|
* Used when Hubert interface first loads
|
||||||
|
*/
|
||||||
|
export const POST = async (context) => {
|
||||||
|
try {
|
||||||
|
const { request, env } = context;
|
||||||
|
const userAgent = request.headers.get('user-agent') || 'unknown';
|
||||||
|
const ip = request.headers.get('cf-connecting-ip') || 'unknown';
|
||||||
|
|
||||||
|
const visitorId = randomUUID();
|
||||||
|
|
||||||
|
// Only insert into database if HUBERT_DB binding exists (production)
|
||||||
|
// In dev mode, this allows the chatbot to work without Cloudflare bindings
|
||||||
|
if (env && env.HUBERT_DB) {
|
||||||
|
await env.HUBERT_DB.prepare(`
|
||||||
|
INSERT INTO visitors (visitor_id, first_seen_at, last_seen_at, ip_address, user_agent)
|
||||||
|
VALUES (?, datetime('now'), datetime('now'), ?, ?)
|
||||||
|
`).bind(visitorId, ip, userAgent).run();
|
||||||
|
console.log(`[Hubert] New visitor initialized: ${visitorId}`);
|
||||||
|
} else {
|
||||||
|
console.log(`[Hubert] Dev mode: Skipping database insert for visitor: ${visitorId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
visitor_id: visitorId,
|
||||||
|
conversation_id: visitorId, // Use visitor_id as initial conversation_id
|
||||||
|
status: '/// INTERVIEW_TERMINAL_READY',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Hubert] Failed to initialize visitor:', error);
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: '/// HUBERT_INIT_FAILED',
|
||||||
|
details: String(error),
|
||||||
|
}),
|
||||||
|
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Env {
|
||||||
|
HUBERT_DB: D1Database;
|
||||||
|
OPENROUTER_API_KEY: string;
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import Hero from '../components/sections/Hero.astro';
|
|||||||
import Experience from '../components/sections/Experience.astro';
|
import Experience from '../components/sections/Experience.astro';
|
||||||
import FeaturedProject from '../components/sections/FeaturedProject.astro';
|
import FeaturedProject from '../components/sections/FeaturedProject.astro';
|
||||||
import Skills from '../components/sections/Skills.astro';
|
import Skills from '../components/sections/Skills.astro';
|
||||||
|
import Hubert from '../components/sections/Hubert.astro';
|
||||||
import { getEntry } from 'astro:content';
|
import { getEntry } from 'astro:content';
|
||||||
|
|
||||||
// Fetch all section content
|
// Fetch all section content
|
||||||
@ -11,6 +12,7 @@ const heroEntry = await getEntry('sections', 'hero');
|
|||||||
const experienceEntry = await getEntry('sections', 'experience');
|
const experienceEntry = await getEntry('sections', 'experience');
|
||||||
const skillsEntry = await getEntry('sections', 'skills');
|
const skillsEntry = await getEntry('sections', 'skills');
|
||||||
const featuredProjectEntry = await getEntry('sections', 'featured-project');
|
const featuredProjectEntry = await getEntry('sections', 'featured-project');
|
||||||
|
const hubertEntry = await getEntry('sections', 'hubert');
|
||||||
|
|
||||||
// Extract content from entries
|
// Extract content from entries
|
||||||
const heroContent = {
|
const heroContent = {
|
||||||
@ -49,6 +51,13 @@ const featuredProjectContent = {
|
|||||||
videoUrl: featuredProjectEntry.data.videoUrl || '',
|
videoUrl: featuredProjectEntry.data.videoUrl || '',
|
||||||
linkUrl: featuredProjectEntry.data.linkUrl || '',
|
linkUrl: featuredProjectEntry.data.linkUrl || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hubertContent = {
|
||||||
|
sectionTitle: hubertEntry.data.sectionTitle || '',
|
||||||
|
sectionSubtitle: hubertEntry.data.sectionSubtitle || '',
|
||||||
|
sectionLabel: hubertEntry.data.sectionLabel || '',
|
||||||
|
description: hubertEntry.data.description || '',
|
||||||
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout usePadding={false}>
|
<BaseLayout usePadding={false}>
|
||||||
@ -68,4 +77,6 @@ const featuredProjectContent = {
|
|||||||
|
|
||||||
<FeaturedProject {...featuredProjectContent} />
|
<FeaturedProject {...featuredProjectContent} />
|
||||||
<Skills {...skillsContent} />
|
<Skills {...skillsContent} />
|
||||||
|
|
||||||
|
<Hubert {...hubertContent} />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@ -12,10 +12,10 @@
|
|||||||
"pages_build_output_dir": "./dist",
|
"pages_build_output_dir": "./dist",
|
||||||
"observability": {
|
"observability": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
}
|
},
|
||||||
/**
|
/**
|
||||||
* Smart Placement
|
* Smart Placement
|
||||||
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
|
* Docs: https://developers.cloudflare.com/workers/wrangler/configuration/smart-placement/#smart-placement
|
||||||
*/
|
*/
|
||||||
// "placement": { "mode": "smart" }
|
// "placement": { "mode": "smart" }
|
||||||
/**
|
/**
|
||||||
@ -35,7 +35,7 @@
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Static Assets
|
* Static Assets
|
||||||
* https://developers.cloudflare.com/workers/static-assets/binding/
|
* https:// developers.cloudflare.com/static-assets/binding/
|
||||||
*/
|
*/
|
||||||
// "assets": { "directory": "./public/", "binding": "ASSETS" }
|
// "assets": { "directory": "./public/", "binding": "ASSETS" }
|
||||||
/**
|
/**
|
||||||
@ -43,4 +43,15 @@
|
|||||||
* https:// developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
|
* https:// developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
|
||||||
*/
|
*/
|
||||||
// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
|
// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
|
||||||
|
/**
|
||||||
|
* D1 Database for Hubert Chatbot
|
||||||
|
* Stores visitors, conversations, and messages
|
||||||
|
*/
|
||||||
|
"d1_databases": [
|
||||||
|
{
|
||||||
|
"binding": "HUBERT_DB",
|
||||||
|
"database_name": "hubert-conversations",
|
||||||
|
"database_id": "<your-database-id-from-wrangler-output>"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user