diff --git a/.env.local.example b/.env.local.example index 099ada7f..c29f661f 100644 --- a/.env.local.example +++ b/.env.local.example @@ -13,3 +13,9 @@ NEXT_SEARCH_ACCESS_TOKEN=search-access-token # Google Analytics key NEXT_PUBLIC_GOOGLE_ANALYTICS_KEY=ga-key + +# Google Vertex AI API Key +GOOGLE_VERTEX_CLIENT_EMAIL=example-project-website@example-org.iam.gserviceaccount.com +GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----Your private key here-----END PRIVATE KEY-----" +GOOGLE_VERTEX_LOCATION=us-west1 +GOOGLE_VERTEX_PROJECT=example-project diff --git a/package.json b/package.json index 1b8cb3da..64a267d2 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "prepare": "husky" }, "dependencies": { + "@ai-sdk/google-vertex": "^2.2.27", "@apollo/client": "^3.13.8", "@faustwp/blocks": "^6.1.2", "@faustwp/cli": "^3.2.3", @@ -31,6 +32,7 @@ "@shikijs/transformers": "^3.7.0", "@sindresorhus/slugify": "^2.2.1", "@wpengine/atlas-next": "^3.0.0", + "ai": "^4.3.16", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "feed": "^5.1.0", @@ -44,6 +46,7 @@ "react-dom": "^19.1.0", "react-icons": "^5.5.0", "react-intersection-observer": "^9.16.0", + "react-markdown": "^10.1.0", "rehype-callouts": "^2.1.1", "rehype-external-links": "^3.0.0", "rehype-pretty-code": "^0.14.1", @@ -57,7 +60,8 @@ "shiki": "^3.7.0", "strip-markdown": "^6.0.0", "unified": "^11.0.5", - "vfile-matter": "^5.0.1" + "vfile-matter": "^5.0.1", + "zod": "^3.25.76" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ce5cb03..24560fbc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,18 +17,21 @@ importers: .: dependencies: + '@ai-sdk/google-vertex': + specifier: ^2.2.27 + version: 2.2.27(zod@3.25.76) '@apollo/client': specifier: ^3.13.8 version: 3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@faustwp/blocks': specifier: ^6.1.2 - version: 6.1.2(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faustwp/core@3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@wordpress/style-engine@2.11.0)(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 6.1.2(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faustwp/core@3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@wordpress/style-engine@2.11.0)(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@faustwp/cli': specifier: ^3.2.3 version: 3.2.3(webpack@5.96.1) '@faustwp/core': specifier: ^3.2.3 - version: 3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@headlessui/react': specifier: ^2.2.4 version: 2.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -37,7 +40,7 @@ importers: version: 2.0.2 '@next/third-parties': specifier: ^15.3.5 - version: 15.3.5(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 15.3.5(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@octokit/core': specifier: ^7.0.2 version: 7.0.2 @@ -49,7 +52,10 @@ importers: version: 2.2.1 '@wpengine/atlas-next': specifier: ^3.0.0 - version: 3.0.0(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(node-fetch@2.7.0) + version: 3.0.0(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(node-fetch@2.7.0) + ai: + specifier: ^4.3.16 + version: 4.3.16(react@19.1.0)(zod@3.25.76) date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -70,13 +76,13 @@ importers: version: 4.0.8 next: specifier: ^15.3.5 - version: 15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-mdx-remote-client: specifier: ^2.1.2 version: 2.1.2(@types/react@18.3.12)(acorn@8.15.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(unified@11.0.5) next-sitemap: specifier: ^4.2.3 - version: 4.2.3(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + version: 4.2.3(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) react: specifier: ^19.1.0 version: 19.1.0 @@ -89,6 +95,9 @@ importers: react-intersection-observer: specifier: ^9.16.0 version: 9.16.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@18.3.12)(react@19.1.0) rehype-callouts: specifier: ^2.1.1 version: 2.1.1 @@ -131,6 +140,9 @@ importers: vfile-matter: specifier: ^5.0.1 version: 5.0.1 + zod: + specifier: ^3.25.76 + version: 3.25.76 devDependencies: '@tailwindcss/postcss': specifier: ^4.1.11 @@ -180,6 +192,50 @@ importers: packages: + '@ai-sdk/anthropic@1.2.12': + resolution: {integrity: sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/google-vertex@2.2.27': + resolution: {integrity: sha512-iDGX/2yrU4OOL1p/ENpfl3MWxuqp9/bE22Z8Ip4DtLCUx6ismUNtrKO357igM1/3jrM6t9C6egCPniHqBsHOJA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/google@1.2.22': + resolution: {integrity: sha512-Ppxu3DIieF1G9pyQ5O1Z646GYR0gkC57YdBqXJ82qvCdhEhZHu0TWhmnOoeIWe2olSbuDeoOY+MfJrW8dzS3Hw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@2.2.8': + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider@1.1.3': + resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.2.12': + resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/ui-utils@1.2.11': + resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -748,6 +804,10 @@ packages: '@octokit/types@14.1.0': resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -966,6 +1026,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -1343,6 +1406,20 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + ai@4.3.16: + resolution: {integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -1499,12 +1576,18 @@ packages: bare-events@2.5.4: resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} bent@7.3.12: resolution: {integrity: sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==} + bignumber.js@9.3.0: + resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1531,6 +1614,9 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1825,6 +1911,9 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + diff@5.2.0: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} @@ -1855,6 +1944,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + electron-to-chromium@1.5.166: resolution: {integrity: sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==} @@ -2382,6 +2474,14 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + gaxios@6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} + + gcp-metadata@6.1.1: + resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==} + engines: {node: '>=14'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2461,6 +2561,14 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + google-auth-library@9.15.1: + resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} + engines: {node: '>=14'} + + google-logging-utils@0.0.2: + resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==} + engines: {node: '>=14'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2481,6 +2589,10 @@ packages: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gtoken@7.1.0: + resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} + engines: {node: '>=14.0.0'} + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -2551,12 +2663,19 @@ packages: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} http-status-codes@2.3.0: resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -2850,6 +2969,9 @@ packages: engines: {node: '>=6'} hasBin: true + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -2876,6 +2998,11 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -2883,6 +3010,12 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -3721,6 +3854,12 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react@19.1.0: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} @@ -3966,6 +4105,9 @@ packages: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -4231,6 +4373,11 @@ packages: resolution: {integrity: sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==} engines: {node: '>= 8'} + swr@2.3.4: + resolution: {integrity: sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + symbol-observable@4.0.0: resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} engines: {node: '>=0.10'} @@ -4283,6 +4430,10 @@ packages: third-party-capital@1.0.20: resolution: {integrity: sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==} + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -4474,6 +4625,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} @@ -4664,11 +4819,71 @@ packages: resolution: {integrity: sha512-LfOdrUvPB8ZoXtvOBz6DlNClfvi//b5d56mSWyJi7XbH/HfhOHfUhOqxhT/rUiR7yiktlunqRo+jY6y/cWC/5g==} engines: {node: '>= 12.0.0'} + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: + '@ai-sdk/anthropic@1.2.12(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/google-vertex@2.2.27(zod@3.25.76)': + dependencies: + '@ai-sdk/anthropic': 1.2.12(zod@3.25.76) + '@ai-sdk/google': 1.2.22(zod@3.25.76) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + google-auth-library: 9.15.1 + zod: 3.25.76 + transitivePeerDependencies: + - encoding + - supports-color + + '@ai-sdk/google@1.2.22(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + zod: 3.25.76 + + '@ai-sdk/provider@1.1.3': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@19.1.0)(zod@3.25.76)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + react: 19.1.0 + swr: 2.3.4(react@19.1.0) + throttleit: 2.1.0 + optionalDependencies: + zod: 3.25.76 + + '@ai-sdk/ui-utils@1.2.11(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -4876,12 +5091,12 @@ snapshots: '@eslint/core': 0.14.0 levn: 0.4.1 - '@faustwp/blocks@6.1.2(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faustwp/core@3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@wordpress/style-engine@2.11.0)(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@faustwp/blocks@6.1.2(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faustwp/core@3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@wordpress/style-engine@2.11.0)(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@apollo/client': 3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@faustwp/core': 3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@faustwp/core': 3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@wordpress/style-engine': 2.11.0 - next: 15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -4903,7 +5118,7 @@ snapshots: - webpack-bundle-analyzer - webpack-dev-server - '@faustwp/core@3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@faustwp/core@3.2.3(@apollo/client@3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@apollo/client': 3.13.8(@types/react@18.3.12)(graphql@16.11.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@wordpress/hooks': 3.58.0 @@ -4916,7 +5131,7 @@ snapshots: js-cookie: 3.0.5 js-sha256: 0.9.0 lodash: 4.17.21 - next: 15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -5191,9 +5406,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.3.5': optional: true - '@next/third-parties@15.3.5(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@next/third-parties@15.3.5(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': dependencies: - next: 15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 third-party-capital: 1.0.20 @@ -5302,6 +5517,8 @@ snapshots: dependencies: '@octokit/openapi-types': 25.1.0 + '@opentelemetry/api@1.9.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -5547,6 +5764,8 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/diff-match-patch@1.0.36': {} + '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 @@ -5917,10 +6136,10 @@ snapshots: '@babel/runtime': 7.27.6 change-case: 4.1.2 - '@wpengine/atlas-next@3.0.0(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(node-fetch@2.7.0)': + '@wpengine/atlas-next@3.0.0(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(node-fetch@2.7.0)': dependencies: '@wpengine/edge-cache': 1.3.2 - next: 15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) node-fetch: 2.7.0 '@wpengine/edge-cache@1.3.2': {} @@ -5953,6 +6172,20 @@ snapshots: acorn@8.15.0: {} + agent-base@7.1.3: {} + + ai@4.3.16(react@19.1.0)(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/react': 1.2.12(react@19.1.0)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + zod: 3.25.76 + optionalDependencies: + react: 19.1.0 + ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -6147,6 +6380,8 @@ snapshots: bare-events@2.5.4: optional: true + base64-js@1.5.1: {} + before-after-hook@4.0.0: {} bent@7.3.12: @@ -6155,6 +6390,8 @@ snapshots: caseless: 0.12.0 is-stream: 2.0.1 + bignumber.js@9.3.0: {} + boolbase@1.0.0: {} brace-expansion@1.1.12: @@ -6186,6 +6423,8 @@ snapshots: buffer-crc32@0.2.13: {} + buffer-equal-constant-time@1.0.1: {} + buffer-from@1.1.2: {} builtin-modules@3.3.0: {} @@ -6467,6 +6706,8 @@ snapshots: dependencies: dequal: 2.0.3 + diff-match-patch@1.0.5: {} + diff@5.2.0: {} dir-glob@3.0.1: @@ -6496,6 +6737,10 @@ snapshots: eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + electron-to-chromium@1.5.166: {} electron-to-chromium@1.5.182: {} @@ -7321,6 +7566,26 @@ snapshots: functions-have-names@1.2.3: {} + gaxios@6.7.1: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + is-stream: 2.0.1 + node-fetch: 2.7.0 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + gcp-metadata@6.1.1: + dependencies: + gaxios: 6.7.1 + google-logging-utils: 0.0.2 + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + get-caller-file@2.0.5: {} get-east-asian-width@1.3.0: {} @@ -7422,6 +7687,20 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + google-auth-library@9.15.1: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 6.7.1 + gcp-metadata: 6.1.1 + gtoken: 7.1.0 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + google-logging-utils@0.0.2: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -7435,6 +7714,14 @@ snapshots: graphql@16.11.0: {} + gtoken@7.1.0: + dependencies: + gaxios: 6.7.1 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -7573,10 +7860,19 @@ snapshots: dependencies: lru-cache: 10.4.3 + html-url-attributes@3.0.1: {} + html-void-elements@3.0.0: {} http-status-codes@2.3.0: {} + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + husky@9.1.7: {} ignore@5.3.2: {} @@ -7837,6 +8133,10 @@ snapshots: jsesc@3.1.0: {} + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.0 + json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} @@ -7856,6 +8156,12 @@ snapshots: minimist: 1.2.8 optional: true + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.4.1 + diff-match-patch: 1.0.5 + jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -7869,6 +8175,17 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.0: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -8551,15 +8868,15 @@ snapshots: next-secure-headers@2.2.0: {} - next-sitemap@4.2.3(next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)): + next-sitemap@4.2.3(next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.11 fast-glob: 3.3.3 minimist: 1.2.8 - next: 15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - next@15.3.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.3.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.3.5 '@swc/counter': 0.1.3 @@ -8579,6 +8896,7 @@ snapshots: '@next/swc-linux-x64-musl': 15.3.5 '@next/swc-win32-arm64-msvc': 15.3.5 '@next/swc-win32-x64-msvc': 15.3.5 + '@opentelemetry/api': 1.9.0 sharp: 0.34.2 transitivePeerDependencies: - '@babel/core' @@ -8921,6 +9239,24 @@ snapshots: react-is@16.13.1: {} + react-markdown@10.1.0(@types/react@18.3.12)(react@19.1.0): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 18.3.12 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 19.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react@19.1.0: {} read-package-json-fast@3.0.2: @@ -9289,6 +9625,8 @@ snapshots: refa: 0.12.1 regexp-ast-analysis: 0.7.1 + secure-json-parse@2.7.0: {} + semver@6.3.1: {} semver@7.7.1: {} @@ -9624,6 +9962,12 @@ snapshots: svelte@3.59.2: {} + swr@2.3.4(react@19.1.0): + dependencies: + dequal: 2.0.3 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) + symbol-observable@4.0.0: {} synckit@0.11.8: @@ -9673,6 +10017,8 @@ snapshots: third-party-capital@1.0.20: {} + throttleit@2.1.0: {} + tinyglobby@0.2.14: dependencies: fdir: 6.4.6(picomatch@4.0.2) @@ -9943,6 +10289,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@9.0.1: {} + uvu@0.5.6: dependencies: dequal: 2.0.3 @@ -10209,4 +10557,10 @@ snapshots: compress-commons: 5.0.3 readable-stream: 3.6.2 + zod-to-json-schema@3.24.6(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + zwitch@2.0.4: {} diff --git a/src/app/api/chat/route.js b/src/app/api/chat/route.js new file mode 100644 index 00000000..15102171 --- /dev/null +++ b/src/app/api/chat/route.js @@ -0,0 +1,106 @@ +import { env } from "node:process"; +import { createVertex } from "@ai-sdk/google-vertex"; +import { streamText, convertToCoreMessages } from "ai"; +import { StatusCodes, ReasonPhrases } from "http-status-codes"; +import { smartSearchTool } from "@/lib/rag.mjs"; + +// Ensure all required environment variables are set +if (!env.GOOGLE_VERTEX_PROJECT) { + throw new Error("GOOGLE_VERTEX_PROJECT is not set"); +} + +if (!env.GOOGLE_VERTEX_LOCATION) { + throw new Error("GOOGLE_VERTEX_LOCATION is not set"); +} + +if (!env.GOOGLE_VERTEX_CLIENT_EMAIL) { + throw new Error("GOOGLE_VERTEX_CLIENT_EMAIL is not set"); +} + +if (!env.GOOGLE_VERTEX_PRIVATE_KEY) { + throw new Error("GOOGLE_VERTEX_PRIVATE_KEY is not set"); +} + +if (!env.GOOGLE_VERTEX_PRIVATE_KEY.includes("-----BEGIN PRIVATE KEY-----")) { + throw new Error("GOOGLE_VERTEX_PRIVATE_KEY is not formatted correctly"); +} + +const vertex = createVertex({ + project: env.GOOGLE_VERTEX_PROJECT, + location: env.GOOGLE_VERTEX_LOCATION, + googleAuthOptions: { + credentials: { + client_email: env.GOOGLE_VERTEX_CLIENT_EMAIL, + private_key: env.GOOGLE_VERTEX_PRIVATE_KEY.replaceAll( + String.raw`\n`, + "\n", + ), // Ensure newlines are correctly formatted + }, + }, +}); + +const smartSearchPrompt = ` + - You can use the 'smartSearchTool' to find information relating to Faust. + - WP Engine Smart Search is a powerful tool for finding information about Faust. + - After the 'smartSearchTool' provides results (even if it's an error or no information found) + - You MUST then formulate a conversational response to the user based on those results but also use the tool if the users query is deemed plausible. + - If search results are found, summarize them for the user. + - If no information is found or an error occurs, inform the user clearly. + - IMPORTANT: Don't prefix root-relative links in post_url so client-side routing works. If you find links other places that are at the "faustjs.org" domain, you can make them root-relative. +`; + +const systemPromptContent = ` + - You are a friendly and helpful AI assistant that provides Developers help with their coding tasks and learning, as relevant to Faust.js, WPGraphQL, and headless WordPress. + - Format your responses using Github Flavored Markdown. + - Make sure to format links as [link text](path). + - Make sure to link out to the source of the information you provide. + - Prefer new information over old information. + - Do not invent information. Stick to the data provided by the tool. +`; + +export async function POST(req) { + try { + const { messages } = await req.json(); + + if (!messages || !Array.isArray(messages) || messages.length === 0) { + return new Response(ReasonPhrases.BAD_REQUEST, { + status: StatusCodes.BAD_REQUEST, + }); + } + + const coreMessages = convertToCoreMessages(messages); + + const response = await streamText({ + model: vertex("gemini-2.5-flash"), + system: [systemPromptContent, smartSearchPrompt].join("\n"), + messages: coreMessages, + tools: { + smartSearchTool, + }, + onError: (error) => { + console.error("Error during streaming:", error); + return new Response(ReasonPhrases.INTERNAL_SERVER_ERROR, { + status: StatusCodes.INTERNAL_SERVER_ERROR, + }); + }, + onToolCall: async (toolCall) => { + console.log("Tool call initiated:", toolCall); + }, + onStepFinish: async (result) => { + if (result.usage) { + console.log( + `[Token Usage] Prompt tokens: ${result.usage.promptTokens}, Completion tokens: ${result.usage.completionTokens}, Total tokens: ${result.usage.totalTokens}`, + ); + } + }, + maxSteps: 5, + }); + + return response.toDataStreamResponse(); + } catch (error) { + console.error("Error in chat API:", error); + return new Response(ReasonPhrases.INTERNAL_SERVER_ERROR, { + status: StatusCodes.INTERNAL_SERVER_ERROR, + }); + } +} diff --git a/src/components/chat/chat-button.jsx b/src/components/chat/chat-button.jsx new file mode 100644 index 00000000..1759775a --- /dev/null +++ b/src/components/chat/chat-button.jsx @@ -0,0 +1,62 @@ +import { useEffect, useState } from "react"; +import { + HiOutlineChatBubbleLeftRight, + HiOutlineXCircle, +} from "react-icons/hi2"; +import { useChatDialog } from "./state"; +import { sendChatToggleEvent } from "@/lib/analytics.mjs"; +import { classNames } from "@/utils/strings"; + +export default function ChatButton() { + const { dialog } = useChatDialog(); + const [isOpen, setIsOpen] = useState(false); + const [wasEverOpen, setWasEverOpen] = useState(false); + + useEffect(() => { + const dialogElement = dialog.current; + + const handleDialogToggle = () => { + setIsOpen(!isOpen); + setWasEverOpen(true); + + sendChatToggleEvent({ + is_open: !isOpen, + }); + }; + + dialogElement?.addEventListener("toggle", handleDialogToggle); + + return () => { + dialogElement?.removeEventListener("toggle", handleDialogToggle); + }; + }, [dialog, isOpen, setIsOpen]); + + return ( +
- Powered by{" "}
+ Powered by
Faust.js
- {" "}
- & WP Engine's{" "}
+
+ & WP Engine's