feat: file upload + context compression + fix maxTokens
Deploy LangGraph Server to Azure Web App / build-and-deploy (push) Failing after 16s
Deploy LangGraph UI to Azure Static Web Apps / build-and-deploy (push) Failing after 49s

- Add file-service.ts: Azure Blob upload + PDF/Excel/image parsing
- Add truncate-messages.ts: context compression for long conversations
- Fix maxTokens: flash 500→2000, auto 2048→4096, pro 4096→8192
- Integrate truncateMessages in general-input and router nodes
- Add @azure/storage-blob, pdf-parse, xlsx dependencies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gongzhiyong
2026-04-11 12:31:55 +08:00
co-authored by Claude Sonnet 4.6
parent 9b5b98f675
commit a505e441ad
7 changed files with 661 additions and 6 deletions
+4
View File
@@ -19,6 +19,7 @@
"@assistant-ui/react": "^0.8.0",
"@assistant-ui/react-markdown": "^0.8.0",
"@assistant-ui/react-syntax-highlighter": "^0.7.2",
"@azure/storage-blob": "^12.31.0",
"@faker-js/faker": "^9.5.1",
"@langchain/anthropic": "^0.3.18",
"@langchain/core": "^0.3.45",
@@ -46,6 +47,7 @@
"katex": "^0.16.21",
"lucide-react": "^0.476.0",
"next-themes": "^0.4.4",
"pdf-parse": "^2.4.5",
"prettier": "^3.5.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -62,11 +64,13 @@
"use-query-params": "^2.2.1",
"use-stick-to-bottom": "^1.0.46",
"uuid": "^11.0.5",
"xlsx": "^0.18.5",
"zod": "^3.23.8"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@types/node": "^22.13.5",
"@types/pdf-parse": "^1.1.5",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@types/react-syntax-highlighter": "^15.5.13",
+455
View File
@@ -17,6 +17,9 @@ importers:
'@assistant-ui/react-syntax-highlighter':
specifier: ^0.7.2
version: 0.7.10(@assistant-ui/react-markdown@0.8.0(@assistant-ui/react@0.8.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@assistant-ui/react@0.8.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react-syntax-highlighter@15.5.13)(@types/react@19.0.10)(react-syntax-highlighter@15.6.1(react@19.0.0))(react@19.0.0)
'@azure/storage-blob':
specifier: ^12.31.0
version: 12.31.0
'@faker-js/faker':
specifier: ^9.5.1
version: 9.5.1
@@ -98,6 +101,9 @@ importers:
next-themes:
specifier: ^0.4.4
version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
pdf-parse:
specifier: ^2.4.5
version: 2.4.5
prettier:
specifier: ^3.5.2
version: 3.5.2
@@ -146,6 +152,9 @@ importers:
uuid:
specifier: ^11.0.5
version: 11.1.0
xlsx:
specifier: ^0.18.5
version: 0.18.5
zod:
specifier: ^3.23.8
version: 3.24.2
@@ -156,6 +165,9 @@ importers:
'@types/node':
specifier: ^22.13.5
version: 22.13.5
'@types/pdf-parse':
specifier: ^1.1.5
version: 1.1.5
'@types/react':
specifier: ^19.0.8
version: 19.0.10
@@ -258,6 +270,61 @@ packages:
'@types/react-dom':
optional: true
'@azure/abort-controller@2.1.2':
resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==}
engines: {node: '>=18.0.0'}
'@azure/core-auth@1.10.1':
resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==}
engines: {node: '>=20.0.0'}
'@azure/core-client@1.10.1':
resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==}
engines: {node: '>=20.0.0'}
'@azure/core-http-compat@2.4.0':
resolution: {integrity: sha512-f1P96IB399YiN2ARYHP7EpZi3Bf3wH4SN2lGzrw7JVwm7bbsVYtf2iKSBwTywD2P62NOPZGHFSZi+6jjb75JuA==}
engines: {node: '>=20.0.0'}
peerDependencies:
'@azure/core-client': ^1.10.0
'@azure/core-rest-pipeline': ^1.22.0
'@azure/core-lro@2.7.2':
resolution: {integrity: sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==}
engines: {node: '>=18.0.0'}
'@azure/core-paging@1.6.2':
resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==}
engines: {node: '>=18.0.0'}
'@azure/core-rest-pipeline@1.23.0':
resolution: {integrity: sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==}
engines: {node: '>=20.0.0'}
'@azure/core-tracing@1.3.1':
resolution: {integrity: sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==}
engines: {node: '>=20.0.0'}
'@azure/core-util@1.13.1':
resolution: {integrity: sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==}
engines: {node: '>=20.0.0'}
'@azure/core-xml@1.5.1':
resolution: {integrity: sha512-xcNRHqCoSp4AunOALEae6A8f3qATb83gSrm31Iqb01OzblvC3/W/bfXozcq78EzIdzZzuH1bZ2NvRR0TdX709w==}
engines: {node: '>=20.0.0'}
'@azure/logger@1.3.0':
resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==}
engines: {node: '>=20.0.0'}
'@azure/storage-blob@12.31.0':
resolution: {integrity: sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg==}
engines: {node: '>=20.0.0'}
'@azure/storage-common@12.3.0':
resolution: {integrity: sha512-/OFHhy86aG5Pe8dP5tsp+BuJ25JOAl9yaMU3WZbkeoiFMHFtJ7tu5ili7qEdBXNW9G5lDB19trwyI6V49F/8iQ==}
engines: {node: '>=20.0.0'}
'@babel/code-frame@7.26.2':
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
@@ -700,6 +767,70 @@ packages:
peerDependencies:
'@langchain/core': '>=0.3.39 <0.4.0'
'@napi-rs/canvas-android-arm64@0.1.80':
resolution: {integrity: sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@napi-rs/canvas-darwin-arm64@0.1.80':
resolution: {integrity: sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@napi-rs/canvas-darwin-x64@0.1.80':
resolution: {integrity: sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.80':
resolution: {integrity: sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@napi-rs/canvas-linux-arm64-gnu@0.1.80':
resolution: {integrity: sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@napi-rs/canvas-linux-arm64-musl@0.1.80':
resolution: {integrity: sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@napi-rs/canvas-linux-riscv64-gnu@0.1.80':
resolution: {integrity: sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
'@napi-rs/canvas-linux-x64-gnu@0.1.80':
resolution: {integrity: sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@napi-rs/canvas-linux-x64-musl@0.1.80':
resolution: {integrity: sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@napi-rs/canvas-win32-x64-msvc@0.1.80':
resolution: {integrity: sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@napi-rs/canvas@0.1.80':
resolution: {integrity: sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==}
engines: {node: '>= 10'}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -1252,6 +1383,9 @@ packages:
'@types/node@22.13.5':
resolution: {integrity: sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==}
'@types/pdf-parse@1.1.5':
resolution: {integrity: sha512-kBfrSXsloMnUJOKi25s3+hRmkycHfLK6A09eRGqF/N8BkQoPUmaCr+q8Cli5FnfohEz/rsv82zAiPz/LXtOGhA==}
'@types/prismjs@1.26.5':
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
@@ -1336,6 +1470,10 @@ packages:
peerDependencies:
typescript: '*'
'@typespec/ts-http-runtime@0.3.5':
resolution: {integrity: sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==}
engines: {node: '>=20.0.0'}
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
@@ -1359,6 +1497,14 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
adler-32@1.3.1:
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
engines: {node: '>=0.8'}
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
agentkeepalive@4.6.0:
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
engines: {node: '>= 8.0.0'}
@@ -1458,6 +1604,10 @@ packages:
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
cfb@1.2.2:
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
engines: {node: '>=0.8'}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
@@ -1501,6 +1651,10 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
codepage@1.15.0:
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
engines: {node: '>=0.8'}
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
@@ -1562,6 +1716,11 @@ packages:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
engines: {node: '>=12.13'}
crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
hasBin: true
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -1831,6 +1990,10 @@ packages:
eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
events@3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
execa@9.5.2:
resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==}
engines: {node: ^18.19.0 || >=20.5.0}
@@ -1864,10 +2027,17 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
fast-xml-builder@1.1.4:
resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==}
fast-xml-parser@4.5.3:
resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
hasBin: true
fast-xml-parser@5.5.11:
resolution: {integrity: sha512-QL0eb0YbSTVWF6tTf1+LEMSgtCEjBYPpnAjoLC8SscESlAjXEIRJ7cHtLG0pLeDFaZLa4VKZLArtA/60ZS7vyA==}
hasBin: true
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
@@ -1925,6 +2095,10 @@ packages:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'}
frac@1.1.2:
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
engines: {node: '>=0.8'}
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@@ -2079,6 +2253,14 @@ packages:
html-url-attributes@3.0.1:
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
human-signals@8.0.0:
resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==}
engines: {node: '>=18.18.0'}
@@ -2676,6 +2858,10 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
path-expression-matcher@1.5.0:
resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==}
engines: {node: '>=14.0.0'}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@@ -2688,6 +2874,15 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
pdf-parse@2.4.5:
resolution: {integrity: sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg==}
engines: {node: '>=20.16.0 <21 || >=22.3.0'}
hasBin: true
pdfjs-dist@5.4.296:
resolution: {integrity: sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==}
engines: {node: '>=20.16.0 || >=22.3.0'}
pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
@@ -3066,6 +3261,10 @@ packages:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
ssf@0.11.2:
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
engines: {node: '>=0.8'}
stack-trace@0.0.10:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
@@ -3109,6 +3308,9 @@ packages:
strnum@1.1.2:
resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==}
strnum@2.2.3:
resolution: {integrity: sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==}
style-to-object@1.0.8:
resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==}
@@ -3406,10 +3608,18 @@ packages:
resolution: {integrity: sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==}
engines: {node: '>= 12.0.0'}
wmf@1.0.2:
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
engines: {node: '>=0.8'}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
word@0.3.0:
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
engines: {node: '>=0.8'}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -3421,6 +3631,11 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
xlsx@0.18.5:
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
engines: {node: '>=0.8'}
hasBin: true
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
@@ -3556,6 +3771,119 @@ snapshots:
- immer
- use-sync-external-store
'@azure/abort-controller@2.1.2':
dependencies:
tslib: 2.8.1
'@azure/core-auth@1.10.1':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-util': 1.13.1
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@azure/core-client@1.10.1':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-auth': 1.10.1
'@azure/core-rest-pipeline': 1.23.0
'@azure/core-tracing': 1.3.1
'@azure/core-util': 1.13.1
'@azure/logger': 1.3.0
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@azure/core-http-compat@2.4.0(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0)':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-client': 1.10.1
'@azure/core-rest-pipeline': 1.23.0
'@azure/core-lro@2.7.2':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-util': 1.13.1
'@azure/logger': 1.3.0
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@azure/core-paging@1.6.2':
dependencies:
tslib: 2.8.1
'@azure/core-rest-pipeline@1.23.0':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-auth': 1.10.1
'@azure/core-tracing': 1.3.1
'@azure/core-util': 1.13.1
'@azure/logger': 1.3.0
'@typespec/ts-http-runtime': 0.3.5
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@azure/core-tracing@1.3.1':
dependencies:
tslib: 2.8.1
'@azure/core-util@1.13.1':
dependencies:
'@azure/abort-controller': 2.1.2
'@typespec/ts-http-runtime': 0.3.5
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@azure/core-xml@1.5.1':
dependencies:
fast-xml-parser: 5.5.11
tslib: 2.8.1
'@azure/logger@1.3.0':
dependencies:
'@typespec/ts-http-runtime': 0.3.5
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@azure/storage-blob@12.31.0':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-auth': 1.10.1
'@azure/core-client': 1.10.1
'@azure/core-http-compat': 2.4.0(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0)
'@azure/core-lro': 2.7.2
'@azure/core-paging': 1.6.2
'@azure/core-rest-pipeline': 1.23.0
'@azure/core-tracing': 1.3.1
'@azure/core-util': 1.13.1
'@azure/core-xml': 1.5.1
'@azure/logger': 1.3.0
'@azure/storage-common': 12.3.0(@azure/core-client@1.10.1)
events: 3.3.0
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@azure/storage-common@12.3.0(@azure/core-client@1.10.1)':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-auth': 1.10.1
'@azure/core-http-compat': 2.4.0(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0)
'@azure/core-rest-pipeline': 1.23.0
'@azure/core-tracing': 1.3.1
'@azure/core-util': 1.13.1
'@azure/logger': 1.3.0
events: 3.3.0
tslib: 2.8.1
transitivePeerDependencies:
- '@azure/core-client'
- supports-color
'@babel/code-frame@7.26.2':
dependencies:
'@babel/helper-validator-identifier': 7.25.9
@@ -4026,6 +4354,49 @@ snapshots:
- encoding
- ws
'@napi-rs/canvas-android-arm64@0.1.80':
optional: true
'@napi-rs/canvas-darwin-arm64@0.1.80':
optional: true
'@napi-rs/canvas-darwin-x64@0.1.80':
optional: true
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.80':
optional: true
'@napi-rs/canvas-linux-arm64-gnu@0.1.80':
optional: true
'@napi-rs/canvas-linux-arm64-musl@0.1.80':
optional: true
'@napi-rs/canvas-linux-riscv64-gnu@0.1.80':
optional: true
'@napi-rs/canvas-linux-x64-gnu@0.1.80':
optional: true
'@napi-rs/canvas-linux-x64-musl@0.1.80':
optional: true
'@napi-rs/canvas-win32-x64-msvc@0.1.80':
optional: true
'@napi-rs/canvas@0.1.80':
optionalDependencies:
'@napi-rs/canvas-android-arm64': 0.1.80
'@napi-rs/canvas-darwin-arm64': 0.1.80
'@napi-rs/canvas-darwin-x64': 0.1.80
'@napi-rs/canvas-linux-arm-gnueabihf': 0.1.80
'@napi-rs/canvas-linux-arm64-gnu': 0.1.80
'@napi-rs/canvas-linux-arm64-musl': 0.1.80
'@napi-rs/canvas-linux-riscv64-gnu': 0.1.80
'@napi-rs/canvas-linux-x64-gnu': 0.1.80
'@napi-rs/canvas-linux-x64-musl': 0.1.80
'@napi-rs/canvas-win32-x64-msvc': 0.1.80
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -4511,6 +4882,10 @@ snapshots:
dependencies:
undici-types: 6.20.0
'@types/pdf-parse@1.1.5':
dependencies:
'@types/node': 22.13.5
'@types/prismjs@1.26.5': {}
'@types/react-dom@19.0.4(@types/react@19.0.10)':
@@ -4624,6 +4999,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typespec/ts-http-runtime@0.3.5':
dependencies:
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
'@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-react@4.3.4(vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0))':
@@ -4647,6 +5030,10 @@ snapshots:
acorn@8.14.0: {}
adler-32@1.3.1: {}
agent-base@7.1.4: {}
agentkeepalive@4.6.0:
dependencies:
humanize-ms: 1.2.1
@@ -4740,6 +5127,11 @@ snapshots:
ccount@2.0.1: {}
cfb@1.2.2:
dependencies:
adler-32: 1.3.1
crc-32: 1.2.2
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
@@ -4773,6 +5165,8 @@ snapshots:
clsx@2.1.1: {}
codepage@1.15.0: {}
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
@@ -4828,6 +5222,8 @@ snapshots:
dependencies:
is-what: 4.1.16
crc-32@1.2.2: {}
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -5099,6 +5495,8 @@ snapshots:
eventemitter3@4.0.7: {}
events@3.3.0: {}
execa@9.5.2:
dependencies:
'@sindresorhus/merge-streams': 4.0.0
@@ -5144,10 +5542,20 @@ snapshots:
fast-levenshtein@2.0.6: {}
fast-xml-builder@1.1.4:
dependencies:
path-expression-matcher: 1.5.0
fast-xml-parser@4.5.3:
dependencies:
strnum: 1.1.2
fast-xml-parser@5.5.11:
dependencies:
fast-xml-builder: 1.1.4
path-expression-matcher: 1.5.0
strnum: 2.2.3
fastq@1.19.1:
dependencies:
reusify: 1.1.0
@@ -5209,6 +5617,8 @@ snapshots:
node-domexception: 1.0.0
web-streams-polyfill: 4.0.0-beta.3
frac@1.1.2: {}
fraction.js@4.3.7: {}
framer-motion@12.4.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
@@ -5403,6 +5813,20 @@ snapshots:
html-url-attributes@3.0.1: {}
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.4
debug: 4.4.0
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
debug: 4.4.0
transitivePeerDependencies:
- supports-color
human-signals@8.0.0: {}
humanize-ms@1.2.1:
@@ -6170,6 +6594,8 @@ snapshots:
path-exists@4.0.0: {}
path-expression-matcher@1.5.0: {}
path-key@3.1.1: {}
path-key@4.0.0: {}
@@ -6179,6 +6605,15 @@ snapshots:
lru-cache: 10.4.3
minipass: 7.1.2
pdf-parse@2.4.5:
dependencies:
'@napi-rs/canvas': 0.1.80
pdfjs-dist: 5.4.296
pdfjs-dist@5.4.296:
optionalDependencies:
'@napi-rs/canvas': 0.1.80
pend@1.2.0: {}
pg-cloudflare@1.3.0:
@@ -6609,6 +7044,10 @@ snapshots:
split2@4.2.0: {}
ssf@0.11.2:
dependencies:
frac: 1.1.2
stack-trace@0.0.10: {}
stacktrace-parser@0.1.11:
@@ -6652,6 +7091,8 @@ snapshots:
strnum@1.1.2: {}
strnum@2.2.3: {}
style-to-object@1.0.8:
dependencies:
inline-style-parser: 0.2.4
@@ -6936,8 +7377,12 @@ snapshots:
triple-beam: 1.4.1
winston-transport: 4.9.0
wmf@1.0.2: {}
word-wrap@1.2.5: {}
word@0.3.0: {}
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
@@ -6952,6 +7397,16 @@ snapshots:
wrappy@1.0.2: {}
xlsx@0.18.5:
dependencies:
adler-32: 1.3.1
cfb: 1.2.2
codepage: 1.15.0
crc-32: 1.2.2
ssf: 0.11.2
wmf: 1.0.2
word: 0.3.0
xtend@4.0.2: {}
yallist@3.1.1: {}
@@ -1,6 +1,7 @@
import { SupervisorState, SupervisorUpdate } from "../types";
import { ALL_TOOL_DESCRIPTIONS } from "../index";
import { createLlm, type ModelMode } from "@/agent/utils/create-llm";
import { truncateMessages } from "@/agent/utils/truncate-messages";
import { LangGraphRunnableConfig } from "@langchain/langgraph";
export async function generalInput(
@@ -17,12 +18,13 @@ Otherwise, just answer as normal.`;
const modelMode = ((config.configurable?.modelMode as string) ?? "auto") as ModelMode;
const llm = createLlm({ modelMode });
const truncated = truncateMessages(state.messages);
const response = await llm.invoke([
{
role: "system",
content: GENERAL_INPUT_SYSTEM_PROMPT,
},
...state.messages,
...truncated,
]);
return {
@@ -3,6 +3,7 @@ import { ALL_TOOL_DESCRIPTIONS } from "../index";
import { SupervisorState, SupervisorUpdate } from "../types";
import { formatMessages } from "@/agent/utils/format-messages";
import { createLlm } from "@/agent/utils/create-llm";
import { truncateMessages } from "@/agent/utils/truncate-messages";
export async function router(
state: SupervisorState,
@@ -43,8 +44,9 @@ Routing rules:
IMPORTANT: "写代码" (write code to execute) → coder, NOT writer. "写文档/写报告" (write a document) → writer. The key distinction is whether the user wants code EXECUTED or a document CREATED.`;
const allMessagesButLast = state.messages.slice(0, -1);
const lastMessage = state.messages.at(-1);
const truncated = truncateMessages(state.messages);
const allMessagesButLast = truncated.slice(0, -1);
const lastMessage = truncated.at(-1);
const formattedPreviousMessages = formatMessages(allMessagesButLast);
const formattedLastMessage = lastMessage ? formatMessages([lastMessage]) : "";
+3 -3
View File
@@ -6,9 +6,9 @@ const MODEL_PRESETS: Record<
ModelMode,
{ temperature: number; maxTokens: number }
> = {
flash: { temperature: 0.2, maxTokens: 500 },
pro: { temperature: 0.3, maxTokens: 4096 },
auto: { temperature: 0.2, maxTokens: 2048 },
flash: { temperature: 0.2, maxTokens: 2000 },
pro: { temperature: 0.3, maxTokens: 8192 },
auto: { temperature: 0.2, maxTokens: 4096 },
};
/**
+105
View File
@@ -0,0 +1,105 @@
import {
BlobServiceClient,
StorageSharedKeyCredential,
} from "@azure/storage-blob";
import pdfParse from "pdf-parse";
import * as XLSX from "xlsx";
import { randomUUID } from "crypto";
const CONTAINER_NAME = "chat-uploads";
export type ParsedFile =
| { type: "image"; content: string; url: string }
| { type: "document"; content: string; url: string };
function getBlobServiceClient(): BlobServiceClient {
const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
if (!connectionString) {
throw new Error(
"AZURE_STORAGE_CONNECTION_STRING environment variable is not set",
);
}
return BlobServiceClient.fromConnectionString(connectionString);
}
/**
* Upload a buffer to Azure Blob Storage and return the public URL.
*/
export async function uploadToBlob(
buffer: Buffer,
filename: string,
mimeType: string,
): Promise<string> {
const blobServiceClient = getBlobServiceClient();
const containerClient = blobServiceClient.getContainerClient(CONTAINER_NAME);
// Create container if it doesn't exist (with public blob access for image URLs)
await containerClient.createIfNotExists({ access: "blob" });
// Generate a unique blob name to avoid collisions
const ext = filename.includes(".") ? filename.split(".").pop() : "";
const blobName = `${randomUUID()}${ext ? `.${ext}` : ""}`;
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
await blockBlobClient.uploadData(buffer, {
blobHTTPHeaders: { blobContentType: mimeType },
});
return blockBlobClient.url;
}
const IMAGE_MIME_TYPES = new Set([
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
]);
/**
* Parse a file buffer into a structured result.
* - Images: upload to Blob, return URL (for multimodal LLM)
* - PDF: extract text with pdf-parse
* - Excel: convert to CSV with xlsx
* - Other: treat as text
*/
export async function parseFile(
buffer: Buffer,
mimeType: string,
filename: string,
): Promise<ParsedFile> {
// Images — upload and return URL for vision
if (IMAGE_MIME_TYPES.has(mimeType)) {
const url = await uploadToBlob(buffer, filename, mimeType);
return { type: "image", content: "", url };
}
// PDF
if (mimeType === "application/pdf") {
const data = await pdfParse(buffer);
const url = await uploadToBlob(buffer, filename, mimeType);
return { type: "document", content: data.text, url };
}
// Excel
if (
mimeType ===
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
mimeType === "application/vnd.ms-excel"
) {
const workbook = XLSX.read(buffer, { type: "buffer" });
const csvParts: string[] = [];
for (const sheetName of workbook.SheetNames) {
const sheet = workbook.Sheets[sheetName];
csvParts.push(`--- Sheet: ${sheetName} ---`);
csvParts.push(XLSX.utils.sheet_to_csv(sheet));
}
const content = csvParts.join("\n");
const url = await uploadToBlob(buffer, filename, mimeType);
return { type: "document", content, url };
}
// Other text files
const content = buffer.toString("utf-8");
const url = await uploadToBlob(buffer, filename, mimeType);
return { type: "document", content, url };
}
@@ -0,0 +1,87 @@
import { BaseMessage, SystemMessage, HumanMessage } from "@langchain/core/messages";
const MAX_RECENT_MESSAGES = 20;
/**
* Estimate token count using a simple character-based heuristic.
* ~4 chars per token for English, ~2 chars per token for Chinese.
* We use ~3 as a rough average for mixed content.
*/
function estimateTokens(message: BaseMessage): number {
const content = typeof message.content === "string"
? message.content
: JSON.stringify(message.content);
return Math.ceil(content.length / 3);
}
/**
* Truncate messages to fit within a token budget.
* Strategy:
* - Always keep system messages (if any)
* - Keep the most recent N messages
* - If the history exceeds maxTokens, drop older messages and inject a summary note
*/
export function truncateMessages(
messages: BaseMessage[],
maxTokens = 60000,
): BaseMessage[] {
if (messages.length === 0) return messages;
// Separate system messages from the rest
const systemMessages = messages.filter((m) => m instanceof SystemMessage);
const nonSystemMessages = messages.filter((m) => !(m instanceof SystemMessage));
// Calculate total tokens
const totalTokens = messages.reduce((sum, m) => sum + estimateTokens(m), 0);
// If within budget, return as-is
if (totalTokens <= maxTokens) {
return messages;
}
// Keep only recent messages
const recentMessages = nonSystemMessages.slice(-MAX_RECENT_MESSAGES);
// Check if recent messages alone fit
const recentTokens =
systemMessages.reduce((sum, m) => sum + estimateTokens(m), 0) +
recentMessages.reduce((sum, m) => sum + estimateTokens(m), 0);
if (recentTokens <= maxTokens) {
const droppedCount = nonSystemMessages.length - recentMessages.length;
if (droppedCount > 0) {
const summaryNote = new HumanMessage(
`[System note: ${droppedCount} earlier messages were truncated to save context space.]`,
);
return [...systemMessages, summaryNote, ...recentMessages];
}
return [...systemMessages, ...recentMessages];
}
// Still too large — progressively trim from the front of recent messages
const result: BaseMessage[] = [...systemMessages];
let budget = maxTokens - systemMessages.reduce((sum, m) => sum + estimateTokens(m), 0);
const trimmed: BaseMessage[] = [];
for (let i = recentMessages.length - 1; i >= 0; i--) {
const tokens = estimateTokens(recentMessages[i]);
if (budget - tokens >= 0) {
trimmed.unshift(recentMessages[i]);
budget -= tokens;
} else {
break;
}
}
const droppedCount = nonSystemMessages.length - trimmed.length;
if (droppedCount > 0) {
result.push(
new HumanMessage(
`[System note: ${droppedCount} earlier messages were truncated to save context space.]`,
),
);
}
result.push(...trimmed);
return result;
}