Soham Waghmare commited on
Commit
7d94a77
·
1 Parent(s): 38a7e26

feat: agent mode

Browse files
backend/knet.py CHANGED
@@ -463,7 +463,7 @@ class KNet:
463
  generate_content_config = types.GenerateContentConfig(temperature=temp, response_mime_type="text/plain", safety_settings=safe)
464
 
465
  try:
466
- response = self.genai_client.models.generate_content(model="gemini-2.0-flash", contents=prompt, config=generate_content_config)
467
  if not response:
468
  raise Exception("NO_RESPONSE")
469
 
 
463
  generate_content_config = types.GenerateContentConfig(temperature=temp, response_mime_type="text/plain", safety_settings=safe)
464
 
465
  try:
466
+ response = self.genai_client.models.generate_content(model="gemini-2.5-flash", contents=prompt, config=generate_content_config)
467
  if not response:
468
  raise Exception("NO_RESPONSE")
469
 
frontend/bun.lock CHANGED
@@ -6,7 +6,7 @@
6
  "dependencies": {
7
  "@radix-ui/react-avatar": "^1.1.3",
8
  "@radix-ui/react-checkbox": "^1.1.4",
9
- "@radix-ui/react-dialog": "^1.1.6",
10
  "@radix-ui/react-dropdown-menu": "^2.1.6",
11
  "@radix-ui/react-label": "^2.1.2",
12
  "@radix-ui/react-scroll-area": "^1.2.3",
@@ -19,12 +19,12 @@
19
  "class-variance-authority": "^0.7.1",
20
  "clsx": "^2.1.1",
21
  "d3": "^7.9.0",
22
- "eslint-config-next": "^15.2.4",
23
  "lucide-react": "^0.479.0",
24
  "next": "^15.2.4",
25
  "next-themes": "^0.4.6",
26
- "react": "^19.1.0",
27
- "react-dom": "^19.1.0",
28
  "react-markdown": "^10.1.0",
29
  "react-resizable-panels": "^2.1.7",
30
  "react-syntax-highlighter": "^15.6.1",
@@ -215,7 +215,7 @@
215
 
216
  "@next/env": ["@next/env@15.2.4", "", {}, "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g=="],
217
 
218
- "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.2.4", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ=="],
219
 
220
  "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw=="],
221
 
@@ -265,19 +265,19 @@
265
 
266
  "@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
267
 
268
- "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw=="],
269
 
270
  "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
271
 
272
- "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
273
 
274
  "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA=="],
275
 
276
- "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
277
 
278
- "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA=="],
279
 
280
- "@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
281
 
282
  "@radix-ui/react-label": ["@radix-ui/react-label@2.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw=="],
283
 
@@ -285,7 +285,7 @@
285
 
286
  "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="],
287
 
288
- "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
289
 
290
  "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
291
 
@@ -309,7 +309,9 @@
309
 
310
  "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="],
311
 
312
- "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
 
 
313
 
314
  "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
315
 
@@ -769,7 +771,7 @@
769
 
770
  "eslint": ["eslint@9.23.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw=="],
771
 
772
- "eslint-config-next": ["eslint-config-next@15.2.4", "", { "dependencies": { "@next/eslint-plugin-next": "15.2.4", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-v4gYjd4eYIme8qzaJItpR5MMBXJ0/YV07u7eb50kEnlEmX7yhOjdUdzz70v4fiINYRjLf8X8TbogF0k7wlz6sA=="],
773
 
774
  "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
775
 
@@ -1303,9 +1305,9 @@
1303
 
1304
  "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
1305
 
1306
- "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
1307
 
1308
- "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
1309
 
1310
  "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
1311
 
@@ -1621,18 +1623,86 @@
1621
 
1622
  "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1623
 
1624
- "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1625
 
1626
  "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1627
 
 
 
 
 
1628
  "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1629
 
 
 
 
 
 
 
 
 
 
 
 
 
1630
  "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1631
 
1632
  "@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
1633
 
 
 
 
 
 
 
 
 
1634
  "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1635
 
 
 
 
 
1636
  "@ts-morph/common/minimatch": ["minimatch@7.4.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw=="],
1637
 
1638
  "@typescript-eslint/parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
@@ -1739,6 +1809,22 @@
1739
 
1740
  "@next/eslint-plugin-next/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
1741
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1742
  "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
1743
 
1744
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
@@ -1773,6 +1859,8 @@
1773
 
1774
  "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
1775
 
 
 
1776
  "mdast-util-mdx-jsx/parse-entities/is-alphanumerical/is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
1777
  }
1778
  }
 
6
  "dependencies": {
7
  "@radix-ui/react-avatar": "^1.1.3",
8
  "@radix-ui/react-checkbox": "^1.1.4",
9
+ "@radix-ui/react-dialog": "^1.1.14",
10
  "@radix-ui/react-dropdown-menu": "^2.1.6",
11
  "@radix-ui/react-label": "^2.1.2",
12
  "@radix-ui/react-scroll-area": "^1.2.3",
 
19
  "class-variance-authority": "^0.7.1",
20
  "clsx": "^2.1.1",
21
  "d3": "^7.9.0",
22
+ "eslint-config-next": "15.2.6",
23
  "lucide-react": "^0.479.0",
24
  "next": "^15.2.4",
25
  "next-themes": "^0.4.6",
26
+ "react": "19.1.2",
27
+ "react-dom": "19.1.2",
28
  "react-markdown": "^10.1.0",
29
  "react-resizable-panels": "^2.1.7",
30
  "react-syntax-highlighter": "^15.6.1",
 
215
 
216
  "@next/env": ["@next/env@15.2.4", "", {}, "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g=="],
217
 
218
+ "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.2.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-DkQt+OUzNEABMQvupD+w4FsqkDAmA/otgtwhpExzbYZ4bHnUAHwhDD3PUV0maI1qyiGcoOJGG3qZ0IJyK6w+1A=="],
219
 
220
  "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw=="],
221
 
 
265
 
266
  "@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
267
 
268
+ "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw=="],
269
 
270
  "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
271
 
272
+ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ=="],
273
 
274
  "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA=="],
275
 
276
+ "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="],
277
 
278
+ "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
279
 
280
+ "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
281
 
282
  "@radix-ui/react-label": ["@radix-ui/react-label@2.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw=="],
283
 
 
285
 
286
  "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="],
287
 
288
+ "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
289
 
290
  "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
291
 
 
309
 
310
  "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="],
311
 
312
+ "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
313
+
314
+ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
315
 
316
  "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
317
 
 
771
 
772
  "eslint": ["eslint@9.23.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw=="],
773
 
774
+ "eslint-config-next": ["eslint-config-next@15.2.6", "", { "dependencies": { "@next/eslint-plugin-next": "15.2.6", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-NZHjg3/y3xIBHMaWu6ayuy0MPg4Udt6A/Y3kXr6E0kTc61MxT4A6jTbkOVNRAfVx5SgZTDu6COVqFpiBLBcRaw=="],
775
 
776
  "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
777
 
 
1305
 
1306
  "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
1307
 
1308
+ "react": ["react@19.1.2", "", {}, "sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w=="],
1309
 
1310
+ "react-dom": ["react-dom@19.1.2", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.2" } }, "sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g=="],
1311
 
1312
  "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
1313
 
 
1623
 
1624
  "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1625
 
1626
+ "@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
1627
+
1628
+ "@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
1629
+
1630
+ "@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
1631
+
1632
+ "@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
1633
+
1634
+ "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
1635
+
1636
+ "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
1637
+
1638
+ "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
1639
+
1640
+ "@radix-ui/react-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
1641
+
1642
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
1643
+
1644
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
1645
+
1646
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
1647
+
1648
+ "@radix-ui/react-dropdown-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
1649
+
1650
+ "@radix-ui/react-focus-scope/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
1651
+
1652
+ "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
1653
+
1654
+ "@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
1655
+
1656
+ "@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
1657
+
1658
+ "@radix-ui/react-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
1659
+
1660
+ "@radix-ui/react-menu/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
1661
+
1662
+ "@radix-ui/react-menu/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA=="],
1663
+
1664
+ "@radix-ui/react-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
1665
+
1666
+ "@radix-ui/react-menu/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
1667
 
1668
  "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1669
 
1670
+ "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
1671
+
1672
+ "@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
1673
+
1674
  "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1675
 
1676
+ "@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
1677
+
1678
+ "@radix-ui/react-select/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
1679
+
1680
+ "@radix-ui/react-select/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
1681
+
1682
+ "@radix-ui/react-select/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA=="],
1683
+
1684
+ "@radix-ui/react-select/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
1685
+
1686
+ "@radix-ui/react-select/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
1687
+
1688
  "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1689
 
1690
  "@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
1691
 
1692
+ "@radix-ui/react-tabs/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
1693
+
1694
+ "@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
1695
+
1696
+ "@radix-ui/react-tooltip/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
1697
+
1698
+ "@radix-ui/react-tooltip/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
1699
+
1700
  "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
1701
 
1702
+ "@radix-ui/react-use-effect-event/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
1703
+
1704
+ "@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
1705
+
1706
  "@ts-morph/common/minimatch": ["minimatch@7.4.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw=="],
1707
 
1708
  "@typescript-eslint/parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
 
1809
 
1810
  "@next/eslint-plugin-next/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
1811
 
1812
+ "@radix-ui/react-dialog/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
1813
+
1814
+ "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
1815
+
1816
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
1817
+
1818
+ "@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
1819
+
1820
+ "@radix-ui/react-menu/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
1821
+
1822
+ "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
1823
+
1824
+ "@radix-ui/react-select/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
1825
+
1826
+ "@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
1827
+
1828
  "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
1829
 
1830
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
 
1859
 
1860
  "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
1861
 
1862
+ "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
1863
+
1864
  "mdast-util-mdx-jsx/parse-entities/is-alphanumerical/is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
1865
  }
1866
  }
frontend/package.json CHANGED
@@ -11,7 +11,7 @@
11
  "dependencies": {
12
  "@radix-ui/react-avatar": "^1.1.3",
13
  "@radix-ui/react-checkbox": "^1.1.4",
14
- "@radix-ui/react-dialog": "^1.1.6",
15
  "@radix-ui/react-dropdown-menu": "^2.1.6",
16
  "@radix-ui/react-label": "^2.1.2",
17
  "@radix-ui/react-scroll-area": "^1.2.3",
@@ -24,12 +24,12 @@
24
  "class-variance-authority": "^0.7.1",
25
  "clsx": "^2.1.1",
26
  "d3": "^7.9.0",
27
- "eslint-config-next": "^15.2.4",
28
  "lucide-react": "^0.479.0",
29
  "next": "^15.2.4",
30
  "next-themes": "^0.4.6",
31
- "react": "^19.1.0",
32
- "react-dom": "^19.1.0",
33
  "react-markdown": "^10.1.0",
34
  "react-resizable-panels": "^2.1.7",
35
  "react-syntax-highlighter": "^15.6.1",
 
11
  "dependencies": {
12
  "@radix-ui/react-avatar": "^1.1.3",
13
  "@radix-ui/react-checkbox": "^1.1.4",
14
+ "@radix-ui/react-dialog": "^1.1.14",
15
  "@radix-ui/react-dropdown-menu": "^2.1.6",
16
  "@radix-ui/react-label": "^2.1.2",
17
  "@radix-ui/react-scroll-area": "^1.2.3",
 
24
  "class-variance-authority": "^0.7.1",
25
  "clsx": "^2.1.1",
26
  "d3": "^7.9.0",
27
+ "eslint-config-next": "15.2.6",
28
  "lucide-react": "^0.479.0",
29
  "next": "^15.2.4",
30
  "next-themes": "^0.4.6",
31
+ "react": "19.1.2",
32
+ "react-dom": "19.1.2",
33
  "react-markdown": "^10.1.0",
34
  "react-resizable-panels": "^2.1.7",
35
  "react-syntax-highlighter": "^15.6.1",
frontend/src/components/ChatHistory.tsx CHANGED
@@ -1,20 +1,18 @@
1
  "use client";
2
- import { ScrollArea } from "@/components/ui/scroll-area";
3
  import { useChatContext } from "@/lib/store/ChatContext";
4
  import { Message as MessageType } from "@/lib/types";
5
- import { v4 as uuidv4 } from "uuid";
6
- import React, { useEffect, useRef, useState } from "react";
7
  import Message from "./Message";
8
 
9
  interface ChatHistoryProps {
10
  messages: MessageType[];
11
  isLoading: boolean;
 
12
  }
13
 
14
  // Traditional prop-based component
15
- const ChatHistory: React.FC<ChatHistoryProps> = ({ messages, isLoading }) => {
16
  const messagesEndRef = useRef<HTMLDivElement>(null);
17
- const [lastProgressMessage, setLastProgressMessage] = useState<string>("");
18
 
19
  useEffect(() => {
20
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
@@ -30,8 +28,9 @@ const ChatHistory: React.FC<ChatHistoryProps> = ({ messages, isLoading }) => {
30
  </div>
31
  </div>
32
  ) : (
33
- messages.map((message) => {
34
- return <Message key={message.id} message={message} lastProgressMessage={lastProgressMessage} setLastProgressMessage={setLastProgressMessage} />;
 
35
  })
36
  )}
37
 
@@ -42,8 +41,8 @@ const ChatHistory: React.FC<ChatHistoryProps> = ({ messages, isLoading }) => {
42
 
43
  // Context-based component
44
  export const ChatHistoryWithContext: React.FC = () => {
45
- const { chatState } = useChatContext();
46
- return <ChatHistory messages={chatState.messages} isLoading={chatState.isLoading} />;
47
  };
48
 
49
  export default ChatHistory;
 
1
  "use client";
 
2
  import { useChatContext } from "@/lib/store/ChatContext";
3
  import { Message as MessageType } from "@/lib/types";
4
+ import React, { useEffect, useRef } from "react";
 
5
  import Message from "./Message";
6
 
7
  interface ChatHistoryProps {
8
  messages: MessageType[];
9
  isLoading: boolean;
10
+ connectionMode: "agent" | "workflow";
11
  }
12
 
13
  // Traditional prop-based component
14
+ const ChatHistory: React.FC<ChatHistoryProps> = ({ messages, isLoading, connectionMode }) => {
15
  const messagesEndRef = useRef<HTMLDivElement>(null);
 
16
 
17
  useEffect(() => {
18
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
 
28
  </div>
29
  </div>
30
  ) : (
31
+ messages.map((message, index) => {
32
+ const isLast = index === messages.length - 1;
33
+ return <Message key={message.id} message={message} isLoading={isLast && isLoading} connectionMode={connectionMode} />;
34
  })
35
  )}
36
 
 
41
 
42
  // Context-based component
43
  export const ChatHistoryWithContext: React.FC = () => {
44
+ const { chatState, connectionMode } = useChatContext();
45
+ return <ChatHistory messages={chatState.messages} isLoading={chatState.isLoading} connectionMode={connectionMode} />;
46
  };
47
 
48
  export default ChatHistory;
frontend/src/components/Message.tsx CHANGED
@@ -4,13 +4,39 @@ import { Button } from "@/components/ui/button";
4
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
5
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
6
  import { Message as MessageType, ResearchTree } from "@/lib/types";
7
- import { Bot, Copy, MoreHorizontal, User2, ExternalLink, Loader2, SearchIcon } from "lucide-react";
8
  import React, { useState, useEffect, useMemo, useRef } from "react";
9
  import ReactMarkdown from "react-markdown";
10
  import remarkGfm from "remark-gfm";
11
  import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
12
  import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
13
  import { Badge } from "@/components/ui/badge";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  // Function to extract all source URLs from the research tree
16
  const extractAllSources = (tree: ResearchTree | undefined): Array<{ text: string; url: string }> => {
@@ -238,15 +264,14 @@ const MarkdownComponents: Record<string, React.ComponentType<any>> = {
238
  interface MessageProps {
239
  message: MessageType;
240
  isLoading?: boolean;
241
- lastProgressMessage?: string;
242
- setLastProgressMessage?: React.Dispatch<React.SetStateAction<string>>;
243
  }
244
 
245
- const Message = ({ message, isLoading, lastProgressMessage, setLastProgressMessage }: MessageProps) => {
246
  const isUser = message?.role === "user";
247
  const [imageUrls, setImageUrls] = useState<string[]>([]);
248
  const progressPercentage = message.progress || 0;
249
- const isProgressMessage = message.isProgress === true;
250
  const [isSearchMessage, setIsSearchMessage] = useState(message.content.startsWith("s_"));
251
 
252
  // Use useMemo to extract sources only once and only when they change
@@ -285,10 +310,7 @@ const Message = ({ message, isLoading, lastProgressMessage, setLastProgressMessa
285
  }
286
 
287
  setIsSearchMessage(message.content.startsWith("s_"));
288
- if ((isLoading || isProgressMessage) && !message.content.startsWith("s_")) {
289
- setLastProgressMessage!(message.content);
290
- }
291
- }, [message.content, message.media, isUser, isLoading, isProgressMessage, setLastProgressMessage]);
292
 
293
  const copyToClipboard = () => {
294
  if (!isLoading) {
@@ -305,7 +327,7 @@ const Message = ({ message, isLoading, lastProgressMessage, setLastProgressMessa
305
  <div className={`flex items-center gap-2 mb-1 ${isUser ? "justify-end" : "justify-start"}`}>
306
  <div className="font-medium">{isUser ? "You" : "KNet"}</div>
307
  {!isUser && !isLoading && <div className="text-xs text-muted-foreground">{new Date(message.timestamp).toLocaleTimeString()}</div>}
308
- {isLoading && <div className="text-xs text-muted-foreground">Just now</div>}
309
 
310
  {!isUser && !isLoading && !isProgressMessage && (
311
  <div className="ml-auto flex items-center gap-2">
@@ -337,9 +359,9 @@ const Message = ({ message, isLoading, lastProgressMessage, setLastProgressMessa
337
  </div>
338
 
339
  <div className={`mt-1 w-full ${isUser ? "bg-slate-300 dark:bg-slate-200 dark:text-background text-foreground" : "bg-muted/50"} p-3 rounded-2xl ${isUser ? "rounded-tr-sm" : "rounded-tl-sm"}`} style={{ overflowWrap: "anywhere" }}>
340
- {isLoading || isProgressMessage ? (
341
  <div className="space-y-2">
342
- <div>{lastProgressMessage}</div>
343
  <div className={`flex items-center ${isSearchMessage ? "justify-between" : "justify-end"} text-sm w-full`}>
344
  {isSearchMessage ? (
345
  <Badge variant="outline" className="bg-primary/20 text-primary rounded-full">
@@ -359,9 +381,20 @@ const Message = ({ message, isLoading, lastProgressMessage, setLastProgressMessa
359
  </div>
360
  ) : (
361
  <>
362
- <ReactMarkdown remarkPlugins={[remarkGfm]} components={MarkdownComponents}>
363
- {message.content}
364
- </ReactMarkdown>
 
 
 
 
 
 
 
 
 
 
 
365
 
366
  {sourceLinks.length > 0 && <SourceLinks links={sourceLinks} />}
367
  {imageUrls.length > 0 && <ImageGallery imageUrls={imageUrls} />}
 
4
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
5
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
6
  import { Message as MessageType, ResearchTree } from "@/lib/types";
7
+ import { Bot, Copy, MoreHorizontal, User2, ExternalLink, Loader2, SearchIcon, Terminal, ChevronDown, ChevronRight } from "lucide-react";
8
  import React, { useState, useEffect, useMemo, useRef } from "react";
9
  import ReactMarkdown from "react-markdown";
10
  import remarkGfm from "remark-gfm";
11
  import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
12
  import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
13
  import { Badge } from "@/components/ui/badge";
14
+ import { Card, CardContent, CardHeader } from "@/components/ui/card";
15
+
16
+ // Component to display tool calls nicely
17
+ const ToolCallDisplay = ({ toolCall }: { toolCall: any }) => {
18
+ const getDisplayArgs = () => {
19
+ if (!toolCall.args) return "";
20
+ if (typeof toolCall.args === "string") return toolCall.args;
21
+ if (toolCall.args.query) return toolCall.args.query;
22
+ if (toolCall.args.url) return toolCall.args.url;
23
+ return JSON.stringify(toolCall.args);
24
+ };
25
+
26
+ const args = getDisplayArgs();
27
+
28
+ return (
29
+ <div className="my-1.5 border rounded-md bg-background/50 overflow-hidden group hover:bg-muted/30 transition-colors cursor-pointer">
30
+ <div className="flex items-start px-3 py-2 gap-2 text-xs font-mono text-muted-foreground">
31
+ <Terminal className="h-3 w-3 mt-0.5 flex-shrink-0" />
32
+ <span className="font-semibold flex-shrink-0">{toolCall.name}</span>
33
+ <span className="flex-1 truncate group-hover:whitespace-pre-wrap group-hover:break-words transition-all">
34
+ {args}
35
+ </span>
36
+ </div>
37
+ </div>
38
+ );
39
+ };
40
 
41
  // Function to extract all source URLs from the research tree
42
  const extractAllSources = (tree: ResearchTree | undefined): Array<{ text: string; url: string }> => {
 
264
  interface MessageProps {
265
  message: MessageType;
266
  isLoading?: boolean;
267
+ connectionMode: "agent" | "workflow";
 
268
  }
269
 
270
+ const Message = ({ message, isLoading, connectionMode }: MessageProps) => {
271
  const isUser = message?.role === "user";
272
  const [imageUrls, setImageUrls] = useState<string[]>([]);
273
  const progressPercentage = message.progress || 0;
274
+ const isProgressMessage = message.isProgress === true && connectionMode === "workflow";
275
  const [isSearchMessage, setIsSearchMessage] = useState(message.content.startsWith("s_"));
276
 
277
  // Use useMemo to extract sources only once and only when they change
 
310
  }
311
 
312
  setIsSearchMessage(message.content.startsWith("s_"));
313
+ }, [message.content, message.media, isUser, isLoading]);
 
 
 
314
 
315
  const copyToClipboard = () => {
316
  if (!isLoading) {
 
327
  <div className={`flex items-center gap-2 mb-1 ${isUser ? "justify-end" : "justify-start"}`}>
328
  <div className="font-medium">{isUser ? "You" : "KNet"}</div>
329
  {!isUser && !isLoading && <div className="text-xs text-muted-foreground">{new Date(message.timestamp).toLocaleTimeString()}</div>}
330
+ {(isLoading || isProgressMessage) && connectionMode === "workflow" && <div className="text-xs text-muted-foreground">Just now</div>}
331
 
332
  {!isUser && !isLoading && !isProgressMessage && (
333
  <div className="ml-auto flex items-center gap-2">
 
359
  </div>
360
 
361
  <div className={`mt-1 w-full ${isUser ? "bg-slate-300 dark:bg-slate-200 dark:text-background text-foreground" : "bg-muted/50"} p-3 rounded-2xl ${isUser ? "rounded-tr-sm" : "rounded-tl-sm"}`} style={{ overflowWrap: "anywhere" }}>
362
+ {(isLoading || isProgressMessage) && connectionMode === "workflow" ? (
363
  <div className="space-y-2">
364
+ <div>{message.content}</div>
365
  <div className={`flex items-center ${isSearchMessage ? "justify-between" : "justify-end"} text-sm w-full`}>
366
  {isSearchMessage ? (
367
  <Badge variant="outline" className="bg-primary/20 text-primary rounded-full">
 
381
  </div>
382
  ) : (
383
  <>
384
+ {message.content && message.content.trim() !== "" && (
385
+ <ReactMarkdown remarkPlugins={[remarkGfm]} components={MarkdownComponents}>
386
+ {message.content}
387
+ </ReactMarkdown>
388
+ )}
389
+
390
+ {message.tool_calls && message.tool_calls.length > 0 && (
391
+ <div className={`${message.content && message.content.trim() !== "" ? "mt-3" : ""} space-y-2`}>
392
+ <div className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-1">Tool Usage</div>
393
+ {message.tool_calls.map((toolCall, index) => (
394
+ <ToolCallDisplay key={index} toolCall={toolCall} />
395
+ ))}
396
+ </div>
397
+ )}
398
 
399
  {sourceLinks.length > 0 && <SourceLinks links={sourceLinks} />}
400
  {imageUrls.length > 0 && <ImageGallery imageUrls={imageUrls} />}
frontend/src/components/ResearchControls.tsx CHANGED
@@ -49,6 +49,17 @@ const ResearchControls: React.FC<ResearchControlsProps> = ({ options, onOptionCh
49
  Include citations
50
  </Label>
51
  </div>
 
 
 
 
 
 
 
 
 
 
 
52
  </div>
53
 
54
  <Separator />
 
49
  Include citations
50
  </Label>
51
  </div>
52
+
53
+ <div className="flex items-center space-x-2">
54
+ <Checkbox
55
+ id="create_report"
56
+ checked={options.create_report}
57
+ onCheckedChange={(checked) => onOptionChange({ ...options, create_report: checked as boolean })}
58
+ />
59
+ <Label htmlFor="create_report" className="text-sm font-normal">
60
+ Generate Report
61
+ </Label>
62
+ </div>
63
  </div>
64
 
65
  <Separator />
frontend/src/components/ui/ChatLayout.tsx CHANGED
@@ -9,6 +9,7 @@ import React, { useState } from "react";
9
  import { ThemeToggle } from "./ThemeToggle";
10
  import Link from "next/link";
11
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
 
12
  import { Sheet, SheetContent, SheetTrigger, SheetTitle, SheetDescription } from "@/components/ui/sheet";
13
  import ResearchGraph from "@/components/visualizations/ResearchGraph";
14
  import { useChatContext } from "@/lib/store/ChatContext";
@@ -20,7 +21,7 @@ interface ChatLayoutProps {
20
  }
21
 
22
  const ChatLayout: React.FC<ChatLayoutProps> = ({ sidebar, mainContent, settingsPanel }) => {
23
- const { chatState } = useChatContext();
24
  const [activeTab, setActiveTab] = useState<string>("chat");
25
  const [visualizationType, setVisualizationType] = useState<"d3" | "reactflow">("d3");
26
 
@@ -63,6 +64,17 @@ const ChatLayout: React.FC<ChatLayoutProps> = ({ sidebar, mainContent, settingsP
63
  <h1 className="text-xl font-semibold hidden sm:inline">KnowledgeNet: Deep Research</h1>
64
  <h1 className="text-lg font-semibold sm:hidden">KNet: Deep Research</h1>
65
  </Link>
 
 
 
 
 
 
 
 
 
 
 
66
  <div className="flex-1" />
67
  <ThemeToggle />
68
  <Dialog>
 
9
  import { ThemeToggle } from "./ThemeToggle";
10
  import Link from "next/link";
11
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
12
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
13
  import { Sheet, SheetContent, SheetTrigger, SheetTitle, SheetDescription } from "@/components/ui/sheet";
14
  import ResearchGraph from "@/components/visualizations/ResearchGraph";
15
  import { useChatContext } from "@/lib/store/ChatContext";
 
21
  }
22
 
23
  const ChatLayout: React.FC<ChatLayoutProps> = ({ sidebar, mainContent, settingsPanel }) => {
24
+ const { chatState, connectionMode, setConnectionMode } = useChatContext();
25
  const [activeTab, setActiveTab] = useState<string>("chat");
26
  const [visualizationType, setVisualizationType] = useState<"d3" | "reactflow">("d3");
27
 
 
64
  <h1 className="text-xl font-semibold hidden sm:inline">KnowledgeNet: Deep Research</h1>
65
  <h1 className="text-lg font-semibold sm:hidden">KNet: Deep Research</h1>
66
  </Link>
67
+ <div className="ml-4">
68
+ <Select value={connectionMode} onValueChange={(value) => setConnectionMode(value as "agent" | "workflow")}>
69
+ <SelectTrigger className="w-[120px]">
70
+ <SelectValue placeholder="Mode" />
71
+ </SelectTrigger>
72
+ <SelectContent>
73
+ <SelectItem value="agent">Agent</SelectItem>
74
+ <SelectItem value="workflow">Workflow</SelectItem>
75
+ </SelectContent>
76
+ </Select>
77
+ </div>
78
  <div className="flex-1" />
79
  <ThemeToggle />
80
  <Dialog>
frontend/src/lib/socket.ts CHANGED
@@ -1,6 +1,7 @@
1
  import { io, Socket } from "socket.io-client";
2
 
3
  let socket: Socket | null = null;
 
4
 
5
  export const initializeSocket = (url?: string) => {
6
  // In production (Docker), use the same host where the frontend is served
@@ -19,6 +20,18 @@ export const initializeSocket = (url?: string) => {
19
  return socket;
20
  };
21
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  export const getSocket = () => {
23
  if (!socket) {
24
  throw new Error("Socket not initialized. Call initializeSocket first.");
 
1
  import { io, Socket } from "socket.io-client";
2
 
3
  let socket: Socket | null = null;
4
+ let eventSource: EventSource | null = null;
5
 
6
  export const initializeSocket = (url?: string) => {
7
  // In production (Docker), use the same host where the frontend is served
 
20
  return socket;
21
  };
22
 
23
+ export const initializeSse = (url: string) => {
24
+ eventSource = new EventSource(url);
25
+ return eventSource;
26
+ };
27
+
28
+ export const closeSse = () => {
29
+ if (eventSource) {
30
+ eventSource.close();
31
+ eventSource = null;
32
+ }
33
+ };
34
+
35
  export const getSocket = () => {
36
  if (!socket) {
37
  throw new Error("Socket not initialized. Call initializeSocket first.");
frontend/src/lib/store/ChatContext.tsx CHANGED
@@ -3,7 +3,7 @@
3
  import { ChatData, ChatState, Conversation, Message, ResearchOptions, ResearchResults, ResearchTree, StatusUpdate } from "@/lib/types";
4
  import { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
5
  import { v4 as uuidv4 } from "uuid";
6
- import { disconnectSocket, getSocket, initializeSocket } from "@/lib/socket";
7
 
8
  // Utility functions for local storage
9
  const saveToStorage = (data: ChatData) => {
@@ -50,6 +50,7 @@ interface ChatContextType {
50
  currentConversationId: string | null;
51
  researchOptions: ResearchOptions;
52
  userInputRef: React.RefObject<HTMLTextAreaElement>;
 
53
 
54
  // Actions
55
  setResearchOptions: (options: ResearchOptions) => void;
@@ -59,6 +60,7 @@ interface ChatContextType {
59
  deleteConversation: (id: string) => void;
60
  deleteAllConversations: () => void;
61
  abortResearch: () => void; // New function to abort research
 
62
  }
63
 
64
  // Create the context with a default value
@@ -69,12 +71,14 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
69
  const [chatState, setChatState] = useState<ChatState>({ messages: [], isLoading: false, error: null });
70
  const [conversations, setConversations] = useState<Conversation[]>([]);
71
  const [currentConversationId, setCurrentConversationId] = useState<string | null>(null);
 
72
  const [researchOptions, setResearchOptions] = useState<ResearchOptions>({
73
  depth: "basic",
74
  sources: true,
75
  citations: false,
76
  max_depth: 1,
77
  num_sites_per_query: 3,
 
78
  });
79
 
80
  const userInputRef = useRef<HTMLTextAreaElement>(null);
@@ -111,153 +115,155 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
111
  }
112
  }, []);
113
 
114
- // Socket initialization
115
- useEffect(() => {
116
- const socket = initializeSocket();
117
-
118
- socket.on("connect", () => {
119
- console.log("Connected to research server");
120
- });
121
-
122
- socket.on("disconnect", () => {
123
- console.log("Disconnected from research server");
124
-
125
- // When socket disconnects, update any progress messages to show connection error
126
- setChatState((prevState) => {
127
- const updatedMessages = prevState.messages.map((msg) => (msg.isProgress === true ? { ...msg, content: "Connection error", isProgress: false } : msg));
128
-
129
- return {
130
- ...prevState,
131
- messages: updatedMessages,
132
- isLoading: false,
133
- error: "Lost connection to research server",
134
- };
135
- });
136
- });
137
-
138
- socket.on("status", (data: StatusUpdate) => {
139
- setChatState((prevState) => {
140
- const messages = [...prevState.messages];
141
- const progressText = data.message;
142
- const progress = data.progress;
143
-
144
- // Find the last assistant message that is a progress update
145
- const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
146
-
147
- if (lastProgressIndex !== -1) {
148
- // Update existing progress message with research_tree data
149
- messages[lastProgressIndex] = {
150
- ...messages[lastProgressIndex],
151
- content: progressText,
152
- progress: progress,
153
- timestamp: new Date(),
154
- research_tree: data.research_tree, // Update the research_tree in real-time
155
- };
156
- } else {
157
- // Add new progress message with research_tree
158
- messages.push({
159
- id: uuidv4(),
160
- content: progressText,
161
- role: "assistant",
162
- timestamp: new Date(),
163
- progress: progress,
164
- isProgress: true,
165
- research_tree: data.research_tree, // Include the research_tree
166
- media: {}, // Initialize empty media object
167
- });
168
- }
169
-
170
- return {
171
- ...prevState,
172
- messages,
173
- isLoading: true,
174
- };
175
- });
176
- });
177
-
178
- socket.on("research_complete", (results: ResearchResults) => {
179
- setChatState((prevState) => {
180
- const messages = [...prevState.messages];
181
-
182
- // Remove the last progress message if it exists
183
- const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
184
-
185
- if (lastProgressIndex !== -1) {
186
- messages.splice(lastProgressIndex, 1);
187
- }
188
-
189
- const newMessages = [
190
- ...messages,
191
- {
192
- id: uuidv4(),
193
- content: results.content || "Error: No content available",
194
- role: "assistant" as const,
195
- timestamp: new Date(results.timestamp),
196
- media: results.media,
197
- research_tree: results.research_tree,
198
- },
199
- ];
200
-
201
- // Save updated messages to localStorage
202
- const updatedState = {
203
- ...prevState,
204
- isLoading: false,
205
- messages: newMessages,
206
- };
207
-
208
- // Update localStorage with the new messages
209
- const updatedData: ChatData = {
210
- conversations: conversations.map((conv) => ({
211
- ...conv,
212
- messages: conv.id === currentConversationId ? newMessages : conv.messages || [],
213
- lastUpdated: conv.id === currentConversationId ? new Date().toISOString() : conv.lastUpdated,
214
- })),
215
- currentConversationId,
216
- };
217
-
218
- saveToStorage(updatedData);
219
-
220
- return updatedState;
221
- });
222
- });
223
-
224
- socket.on("research_aborted", () => {
225
- setChatState((prevState) => {
226
- const messages = [...prevState.messages];
227
- const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
228
-
229
- if (lastProgressIndex !== -1) {
230
- messages.splice(lastProgressIndex, 1);
231
- }
232
-
233
- // Add a message indicating the research was canceled
234
- messages.push({
235
- id: uuidv4(),
236
- content: "Research has been canceled.",
237
- role: "assistant",
238
- timestamp: new Date(),
239
- });
240
-
241
- return {
242
- ...prevState,
243
- isLoading: false,
244
- messages,
245
- };
246
- });
247
- });
248
-
249
- socket.on("error", (error: { message: string }) => {
250
- setChatState((prevState) => ({
251
- ...prevState,
252
- error: error.message,
253
- isLoading: false,
254
- }));
255
- });
256
-
257
- return () => {
258
- disconnectSocket();
259
- };
260
- }, []);
 
 
261
 
262
  // Save data whenever conversations or messages change
263
  useEffect(() => {
@@ -274,7 +280,7 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
274
 
275
  // Action handlers
276
  const sendMessage = useCallback(
277
- (content: string) => {
278
  if (!content.trim()) return;
279
 
280
  let conversationId = currentConversationId;
@@ -318,30 +324,193 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
318
  );
319
  }
320
 
 
 
 
321
  setChatState((prevState) => ({
322
  ...prevState,
323
- messages: [...prevState.messages, newMessage, newLoadingMessage],
324
  isLoading: true,
325
  error: null,
326
  }));
327
 
328
- // Send message to server via socket
329
  try {
330
- const socket = getSocket();
331
- socket.emit("start_research", {
332
- topic: content,
333
- max_depth: researchOptions.max_depth,
334
- num_sites_per_query: researchOptions.num_sites_per_query,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  } catch (error) {
337
  setChatState((prevState) => ({
338
  ...prevState,
339
- error: "Failed to connect to research server",
340
  isLoading: false,
341
  }));
342
  }
343
  },
344
- [currentConversationId, conversations, researchOptions]
345
  );
346
 
347
  const newConversation = useCallback(() => {
@@ -388,15 +557,39 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
388
  newConversation();
389
  }, [newConversation]);
390
 
391
- const abortResearch = useCallback(() => {
392
  try {
393
- const socket = getSocket();
394
- socket.emit("abort_research");
 
 
 
 
 
 
395
 
396
- setChatState((prevState) => ({
397
- ...prevState,
398
- isLoading: true,
399
- }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  } catch (error) {
401
  console.error("Failed to abort research:", error);
402
  setChatState((prevState) => ({
@@ -404,7 +597,7 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
404
  error: "Failed to abort research",
405
  }));
406
  }
407
- }, []);
408
 
409
  // Keyboard shortcuts | Ctrl + I to new chat
410
  useEffect(() => {
@@ -427,6 +620,7 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
427
  currentConversationId,
428
  researchOptions,
429
  userInputRef,
 
430
  setResearchOptions,
431
  sendMessage,
432
  newConversation,
@@ -434,6 +628,7 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
434
  deleteConversation,
435
  deleteAllConversations,
436
  abortResearch,
 
437
  };
438
 
439
  return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
 
3
  import { ChatData, ChatState, Conversation, Message, ResearchOptions, ResearchResults, ResearchTree, StatusUpdate } from "@/lib/types";
4
  import { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
5
  import { v4 as uuidv4 } from "uuid";
6
+ import { disconnectSocket, getSocket, initializeSocket, initializeSse, closeSse } from "@/lib/socket";
7
 
8
  // Utility functions for local storage
9
  const saveToStorage = (data: ChatData) => {
 
50
  currentConversationId: string | null;
51
  researchOptions: ResearchOptions;
52
  userInputRef: React.RefObject<HTMLTextAreaElement>;
53
+ connectionMode: "agent" | "workflow";
54
 
55
  // Actions
56
  setResearchOptions: (options: ResearchOptions) => void;
 
60
  deleteConversation: (id: string) => void;
61
  deleteAllConversations: () => void;
62
  abortResearch: () => void; // New function to abort research
63
+ setConnectionMode: (mode: "agent" | "workflow") => void;
64
  }
65
 
66
  // Create the context with a default value
 
71
  const [chatState, setChatState] = useState<ChatState>({ messages: [], isLoading: false, error: null });
72
  const [conversations, setConversations] = useState<Conversation[]>([]);
73
  const [currentConversationId, setCurrentConversationId] = useState<string | null>(null);
74
+ const [connectionMode, setConnectionMode] = useState<"agent" | "workflow">("agent");
75
  const [researchOptions, setResearchOptions] = useState<ResearchOptions>({
76
  depth: "basic",
77
  sources: true,
78
  citations: false,
79
  max_depth: 1,
80
  num_sites_per_query: 3,
81
+ create_report: false,
82
  });
83
 
84
  const userInputRef = useRef<HTMLTextAreaElement>(null);
 
115
  }
116
  }, []);
117
 
118
+ // Socket initialization - REMOVED as we are using HTTP/SSE for both modes
119
+ // useEffect(() => {
120
+ // if (connectionMode === "workflow") {
121
+ // const socket = initializeSocket();
122
+
123
+ // socket.on("connect", () => {
124
+ // console.log("Connected to research server");
125
+ // });
126
+
127
+ // socket.on("disconnect", () => {
128
+ // console.log("Disconnected from research server");
129
+
130
+ // // When socket disconnects, update any progress messages to show connection error
131
+ // setChatState((prevState) => {
132
+ // const updatedMessages = prevState.messages.map((msg) => (msg.isProgress === true ? { ...msg, content: "Connection error", isProgress: false } : msg));
133
+
134
+ // return {
135
+ // ...prevState,
136
+ // messages: updatedMessages,
137
+ // isLoading: false,
138
+ // error: "Lost connection to research server",
139
+ // };
140
+ // });
141
+ // });
142
+
143
+ // socket.on("status", (data: StatusUpdate) => {
144
+ // setChatState((prevState) => {
145
+ // const messages = [...prevState.messages];
146
+ // const progressText = data.message;
147
+ // const progress = data.progress;
148
+
149
+ // // Find the last assistant message that is a progress update
150
+ // const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
151
+
152
+ // if (lastProgressIndex !== -1) {
153
+ // // Update existing progress message with research_tree data
154
+ // messages[lastProgressIndex] = {
155
+ // ...messages[lastProgressIndex],
156
+ // content: progressText,
157
+ // progress: progress,
158
+ // timestamp: new Date(),
159
+ // research_tree: data.research_tree, // Update the research_tree in real-time
160
+ // };
161
+ // } else {
162
+ // // Add new progress message with research_tree
163
+ // messages.push({
164
+ // id: uuidv4(),
165
+ // content: progressText,
166
+ // role: "assistant",
167
+ // timestamp: new Date(),
168
+ // progress: progress,
169
+ // isProgress: true,
170
+ // research_tree: data.research_tree, // Include the research_tree
171
+ // media: {}, // Initialize empty media object
172
+ // });
173
+ // }
174
+
175
+ // return {
176
+ // ...prevState,
177
+ // messages,
178
+ // isLoading: true,
179
+ // };
180
+ // });
181
+ // });
182
+
183
+ // socket.on("research_complete", (results: ResearchResults) => {
184
+ // setChatState((prevState) => {
185
+ // const messages = [...prevState.messages];
186
+
187
+ // // Remove the last progress message if it exists
188
+ // const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
189
+
190
+ // if (lastProgressIndex !== -1) {
191
+ // messages.splice(lastProgressIndex, 1);
192
+ // }
193
+
194
+ // const newMessages = [
195
+ // ...messages,
196
+ // {
197
+ // id: uuidv4(),
198
+ // content: results.content || "Error: No content available",
199
+ // role: "assistant" as const,
200
+ // timestamp: new Date(results.timestamp),
201
+ // media: results.media,
202
+ // research_tree: results.research_tree,
203
+ // },
204
+ // ];
205
+
206
+ // // Save updated messages to localStorage
207
+ // const updatedState = {
208
+ // ...prevState,
209
+ // isLoading: false,
210
+ // messages: newMessages,
211
+ // };
212
+
213
+ // // Update localStorage with the new messages
214
+ // const updatedData: ChatData = {
215
+ // conversations: conversations.map((conv) => ({
216
+ // ...conv,
217
+ // messages: conv.id === currentConversationId ? newMessages : conv.messages || [],
218
+ // lastUpdated: conv.id === currentConversationId ? new Date().toISOString() : conv.lastUpdated,
219
+ // })),
220
+ // currentConversationId,
221
+ // };
222
+
223
+ // saveToStorage(updatedData);
224
+
225
+ // return updatedState;
226
+ // });
227
+ // });
228
+
229
+ // socket.on("research_aborted", () => {
230
+ // setChatState((prevState) => {
231
+ // const messages = [...prevState.messages];
232
+ // const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
233
+
234
+ // if (lastProgressIndex !== -1) {
235
+ // messages.splice(lastProgressIndex, 1);
236
+ // }
237
+
238
+ // // Add a message indicating the research was canceled
239
+ // messages.push({
240
+ // id: uuidv4(),
241
+ // content: "Research has been canceled.",
242
+ // role: "assistant",
243
+ // timestamp: new Date(),
244
+ // });
245
+
246
+ // return {
247
+ // ...prevState,
248
+ // isLoading: false,
249
+ // messages,
250
+ // };
251
+ // });
252
+ // });
253
+
254
+ // socket.on("error", (error: { message: string }) => {
255
+ // setChatState((prevState) => ({
256
+ // ...prevState,
257
+ // error: error.message,
258
+ // isLoading: false,
259
+ // }));
260
+ // });
261
+
262
+ // return () => {
263
+ // disconnectSocket();
264
+ // };
265
+ // }
266
+ // }, [connectionMode]);
267
 
268
  // Save data whenever conversations or messages change
269
  useEffect(() => {
 
280
 
281
  // Action handlers
282
  const sendMessage = useCallback(
283
+ async (content: string) => {
284
  if (!content.trim()) return;
285
 
286
  let conversationId = currentConversationId;
 
324
  );
325
  }
326
 
327
+ // Add loading message immediately for workflow mode
328
+ const messagesToAdd = connectionMode === "workflow" ? [newMessage, newLoadingMessage] : [newMessage];
329
+
330
  setChatState((prevState) => ({
331
  ...prevState,
332
+ messages: [...prevState.messages, ...messagesToAdd],
333
  isLoading: true,
334
  error: null,
335
  }));
336
 
 
337
  try {
338
+ const sseUrl = process.env.NEXT_PUBLIC_LANGGRAPH_BACKEND || "http://127.0.0.1:5000";
339
+ const endpoint = connectionMode === "agent" ? "/chat" : "/start_research";
340
+ const body =
341
+ connectionMode === "agent"
342
+ ? {
343
+ message: content,
344
+ thread_id: currentConversationId,
345
+ create_report: researchOptions.create_report,
346
+ }
347
+ : {
348
+ topic: content,
349
+ max_depth: researchOptions.max_depth,
350
+ num_sites_per_query: researchOptions.num_sites_per_query,
351
+ session_id: currentConversationId,
352
+ };
353
+
354
+ const response = await fetch(`${sseUrl}${endpoint}`, {
355
+ method: "POST",
356
+ headers: {
357
+ "Content-Type": "application/json",
358
+ },
359
+ body: JSON.stringify(body),
360
  });
361
+
362
+ if (!response.body) {
363
+ return;
364
+ }
365
+
366
+ const reader = response.body.getReader();
367
+ const decoder = new TextDecoder();
368
+
369
+ let done = false;
370
+ while (!done) {
371
+ const { value, done: readerDone } = await reader.read();
372
+ done = readerDone;
373
+ const chunk = decoder.decode(value, { stream: true });
374
+ const lines = chunk.split("\n\n");
375
+
376
+ for (const line of lines) {
377
+ if (line.startsWith("data: ")) {
378
+ const jsonStr = line.substring(6);
379
+ if (jsonStr) {
380
+ try {
381
+ const data = JSON.parse(jsonStr);
382
+ if (data.event === "progress") {
383
+ setChatState((prevState) => {
384
+ const messages = [...prevState.messages];
385
+
386
+ if (connectionMode === "agent") {
387
+ const eventData = data.data[0]; // Assuming data.data is an array with one element
388
+
389
+ if (eventData.type === "ai_msg" || eventData.type === "ai_msg_report") {
390
+ messages.push({
391
+ id: uuidv4(),
392
+ content: eventData.content,
393
+ role: "assistant",
394
+ timestamp: new Date(),
395
+ tool_calls: eventData.tool_calls, // Store tool calls
396
+ });
397
+ } else if (eventData.type === "tool_resp") {
398
+ // Do not display tool responses directly in chat
399
+ return prevState;
400
+ }
401
+ } else {
402
+ // Workflow mode progress
403
+ const eventData = data.data;
404
+ const progress = eventData.progress;
405
+ const progressText = eventData.message;
406
+
407
+ // Find the last progress message
408
+ const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
409
+
410
+ if (lastProgressIndex !== -1) {
411
+ // Update existing progress message with research_tree data
412
+ messages[lastProgressIndex] = {
413
+ ...messages[lastProgressIndex],
414
+ content: progressText,
415
+ progress: progress,
416
+ timestamp: new Date(),
417
+ research_tree: eventData.research_tree, // Update the research_tree in real-time
418
+ };
419
+ } else {
420
+ // Add new progress message with research_tree
421
+ messages.push({
422
+ id: uuidv4(),
423
+ content: progressText,
424
+ role: "assistant",
425
+ timestamp: new Date(),
426
+ progress: progress,
427
+ isProgress: true,
428
+ research_tree: eventData.research_tree, // Include the research_tree
429
+ media: {}, // Initialize empty media object
430
+ });
431
+ }
432
+ }
433
+
434
+ return {
435
+ ...prevState,
436
+ messages,
437
+ isLoading: true, // Keep loading until stream ends
438
+ };
439
+ });
440
+ } else if (data.event === "result") {
441
+ setChatState((prevState) => {
442
+ const messages = [...prevState.messages];
443
+
444
+ // Remove the last progress message if it exists
445
+ const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
446
+
447
+ if (lastProgressIndex !== -1) {
448
+ messages.splice(lastProgressIndex, 1);
449
+ }
450
+
451
+ const resultData = connectionMode === "agent" ? data.data : data.data; // Both seem to be object in result event?
452
+ // Wait, agent mode result handling was:
453
+ // content: data.data.content || "Error: No content available",
454
+ // media: data.data.media,
455
+ // research_tree: data.data.research_tree,
456
+ //
457
+ // Workflow mode result handling was:
458
+ // content: results.content || "Error: No content available",
459
+ // media: results.media,
460
+ // research_tree: results.research_tree,
461
+ //
462
+ // They look compatible.
463
+
464
+ const newMessages = [
465
+ ...messages,
466
+ {
467
+ id: uuidv4(),
468
+ content: resultData.content || "Error: No content available",
469
+ role: "assistant" as const,
470
+ timestamp: new Date(resultData.timestamp || new Date()),
471
+ media: resultData.media,
472
+ research_tree: resultData.research_tree,
473
+ },
474
+ ];
475
+
476
+ // Save updated messages to localStorage
477
+ const updatedState = {
478
+ ...prevState,
479
+ isLoading: false,
480
+ messages: newMessages,
481
+ };
482
+
483
+ // Update localStorage with the new messages
484
+ const updatedData: ChatData = {
485
+ conversations: conversations.map((conv) => ({
486
+ ...conv,
487
+ messages: conv.id === currentConversationId ? newMessages : conv.messages || [],
488
+ lastUpdated: conv.id === currentConversationId ? new Date().toISOString() : conv.lastUpdated,
489
+ })),
490
+ currentConversationId,
491
+ };
492
+
493
+ saveToStorage(updatedData);
494
+
495
+ return updatedState;
496
+ });
497
+ }
498
+ } catch (e) {
499
+ console.error("Failed to parse SSE data", e);
500
+ }
501
+ }
502
+ }
503
+ }
504
+ }
505
  } catch (error) {
506
  setChatState((prevState) => ({
507
  ...prevState,
508
+ error: "Failed to connect to server",
509
  isLoading: false,
510
  }));
511
  }
512
  },
513
+ [currentConversationId, conversations, researchOptions, connectionMode]
514
  );
515
 
516
  const newConversation = useCallback(() => {
 
557
  newConversation();
558
  }, [newConversation]);
559
 
560
+ const abortResearch = useCallback(async () => {
561
  try {
562
+ const sseUrl = process.env.NEXT_PUBLIC_LANGGRAPH_BACKEND || "http://127.0.0.1:5000";
563
+ await fetch(`${sseUrl}/abort_research`, {
564
+ method: "POST",
565
+ headers: {
566
+ "Content-Type": "application/json",
567
+ },
568
+ body: JSON.stringify({ session_id: currentConversationId }),
569
+ });
570
 
571
+ setChatState((prevState) => {
572
+ const messages = [...prevState.messages];
573
+ const lastProgressIndex = messages.findLastIndex((msg) => msg.role === "assistant" && msg.isProgress === true);
574
+
575
+ if (lastProgressIndex !== -1) {
576
+ messages.splice(lastProgressIndex, 1);
577
+ }
578
+
579
+ // Add a message indicating the research was canceled
580
+ messages.push({
581
+ id: uuidv4(),
582
+ content: "Research has been canceled.",
583
+ role: "assistant",
584
+ timestamp: new Date(),
585
+ });
586
+
587
+ return {
588
+ ...prevState,
589
+ isLoading: false,
590
+ messages,
591
+ };
592
+ });
593
  } catch (error) {
594
  console.error("Failed to abort research:", error);
595
  setChatState((prevState) => ({
 
597
  error: "Failed to abort research",
598
  }));
599
  }
600
+ }, [currentConversationId]);
601
 
602
  // Keyboard shortcuts | Ctrl + I to new chat
603
  useEffect(() => {
 
620
  currentConversationId,
621
  researchOptions,
622
  userInputRef,
623
+ connectionMode,
624
  setResearchOptions,
625
  sendMessage,
626
  newConversation,
 
628
  deleteConversation,
629
  deleteAllConversations,
630
  abortResearch,
631
+ setConnectionMode,
632
  };
633
 
634
  return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
frontend/src/lib/types.ts CHANGED
@@ -24,6 +24,7 @@ export interface ResearchOptions {
24
  citations: boolean;
25
  max_depth: number;
26
  num_sites_per_query: number;
 
27
  }
28
 
29
  export interface ChatData {
 
24
  citations: boolean;
25
  max_depth: number;
26
  num_sites_per_query: number;
27
+ create_report: boolean;
28
  }
29
 
30
  export interface ChatData {
langgraph_backend/agent_tools.py CHANGED
@@ -74,26 +74,25 @@ agent = create_react_agent(
74
  async def invoke_agent(message: str, thread_id: str, idx_retry: int = 1, create_report: bool = False):
75
  config = {"configurable": {"thread_id": thread_id}, "recursion_limit": 50}
76
 
77
- async for event in agent.astream({"messages": [{"role": "user", "content": message}]}, config=config):
78
- print(event)
79
-
80
  if "agent" in event:
81
  response = [
82
  {"type": "ai_msg", "content": m.content, "total_tokens": m.usage_metadata["total_tokens"], "tool_calls": m.tool_calls}
83
  for m in event["agent"]["messages"]
84
  ]
85
- if not event["agent"]["messages"][0].additional_kwargs:
86
- history = [f"{m.type}:\n{m.content}" for m in agent.get_state({"configurable": {"thread_id": "1234"}}).values["messages"]]
 
 
87
  response = [
88
  {
89
  "type": "ai_msg_report",
90
- "content": gen_report("\n\n".join(history), message),
91
- "total_tokens": m.usage_metadata["total_tokens"],
92
- "tool_calls": m.tool_calls,
93
  }
94
- for m in event["agent"]["messages"]
95
  ]
96
 
97
  elif "tools" in event:
98
  response = [{"type": "tool_resp", "content": m.content} for m in event["tools"]["messages"]]
99
- yield response
 
74
  async def invoke_agent(message: str, thread_id: str, idx_retry: int = 1, create_report: bool = False):
75
  config = {"configurable": {"thread_id": thread_id}, "recursion_limit": 50}
76
 
77
+ async for event in agent.astream({"messages": [{"role": "user", "content": message}]}, config=config, stream_mode="updates"):
 
 
78
  if "agent" in event:
79
  response = [
80
  {"type": "ai_msg", "content": m.content, "total_tokens": m.usage_metadata["total_tokens"], "tool_calls": m.tool_calls}
81
  for m in event["agent"]["messages"]
82
  ]
83
+ last_msg = event["agent"]["messages"][-1]
84
+ if not last_msg.tool_calls and create_report:
85
+ history = [f"{m.type}:\n{m.content}" for m in agent.get_state({"configurable": {"thread_id": thread_id}}).values["messages"]]
86
+ report_data = await gen_report("\n\n".join(history), message)
87
  response = [
88
  {
89
  "type": "ai_msg_report",
90
+ "content": report_data,
91
+ "total_tokens": last_msg.usage_metadata["total_tokens"],
92
+ "tool_calls": last_msg.tool_calls,
93
  }
 
94
  ]
95
 
96
  elif "tools" in event:
97
  response = [{"type": "tool_resp", "content": m.content} for m in event["tools"]["messages"]]
98
+ yield {"event": "progress", "data": response}
langgraph_backend/app.py CHANGED
@@ -33,6 +33,7 @@ from schema import (
33
  SearchQuery,
34
  )
35
  from scraper import CrawlForAIScraper
 
36
 
37
  load_dotenv()
38
 
@@ -62,7 +63,7 @@ async def health_check():
62
 
63
 
64
  # --- LangChain LLM setup (Gemini, correct usage) ---
65
- llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", google_api_key=os.getenv("GOOGLE_API_KEY"))
66
 
67
 
68
  class ResearchProgress:
@@ -353,6 +354,30 @@ async def start_research(request: Request):
353
  return EventSourceResponse(event_generator())
354
 
355
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  @app.post("/abort_research")
357
  async def abort_research(request: Request):
358
  data = await request.json()
 
33
  SearchQuery,
34
  )
35
  from scraper import CrawlForAIScraper
36
+ from agent_tools import invoke_agent
37
 
38
  load_dotenv()
39
 
 
63
 
64
 
65
  # --- LangChain LLM setup (Gemini, correct usage) ---
66
+ llm = ChatGoogleGenerativeAI(model="gemini-flash-latest", google_api_key=os.getenv("GOOGLE_API_KEY"))
67
 
68
 
69
  class ResearchProgress:
 
354
  return EventSourceResponse(event_generator())
355
 
356
 
357
+ @app.post("/chat")
358
+ async def chat(request: Request):
359
+ data = await request.json()
360
+ message = data.get("message")
361
+ thread_id = data.get("thread_id")
362
+ create_report = data.get("create_report", False)
363
+
364
+ async def event_generator():
365
+ async for event in invoke_agent(message, thread_id, create_report=create_report):
366
+ # Format the event as SSE (Server-Sent Events)
367
+ event_data = json.dumps(event)
368
+ yield f"data: {event_data}\n\n"
369
+
370
+ return StreamingResponse(
371
+ event_generator(),
372
+ media_type="text/plain",
373
+ headers={
374
+ "Cache-Control": "no-cache",
375
+ "Connection": "keep-alive",
376
+ "Content-Type": "text/event-stream",
377
+ },
378
+ )
379
+
380
+
381
  @app.post("/abort_research")
382
  async def abort_research(request: Request):
383
  data = await request.json()
langgraph_backend/app_tools.py DELETED
@@ -1,80 +0,0 @@
1
- import json
2
- import logging
3
- import os
4
- from datetime import datetime
5
- from typing import Annotated, Any, Dict, List, Literal, Optional, TypedDict
6
-
7
- from dotenv import load_dotenv
8
- from fastapi import FastAPI, Request
9
- from fastapi.middleware.cors import CORSMiddleware
10
- from fastapi.responses import StreamingResponse
11
-
12
- from agent_tools import invoke_agent
13
-
14
- load_dotenv()
15
-
16
- # Today's Date
17
- DATE = datetime.now().strftime("%d %b, %Y")
18
-
19
- logger = logging.getLogger(__name__)
20
- logging.basicConfig(level=logging.INFO)
21
-
22
- app = FastAPI()
23
- CORS_ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", ",").split(",")
24
- app.add_middleware(
25
- CORSMiddleware,
26
- allow_origins=CORS_ALLOWED_ORIGINS,
27
- allow_credentials=True,
28
- allow_methods=["*"],
29
- allow_headers=["*"],
30
- )
31
-
32
- # Session management (in-memory for now)
33
- sessions: Dict[str, Dict[str, Any]] = {}
34
-
35
-
36
- @app.get("/health")
37
- async def health_check():
38
- return {"status": "ok"}
39
-
40
-
41
- @app.post("/chat")
42
- async def chat(request: Request):
43
- data = await request.json()
44
- message = data.get("message")
45
- thread_id = data.get("thread_id")
46
- create_report = data.get("create_report", False)
47
-
48
- async def event_generator():
49
- async for event in invoke_agent(message, thread_id, create_report):
50
- # Format the event as SSE (Server-Sent Events)
51
- event_data = json.dumps(event)
52
- yield f"data: {event_data}\n\n"
53
-
54
- return StreamingResponse(
55
- event_generator(),
56
- media_type="text/plain",
57
- headers={
58
- "Cache-Control": "no-cache",
59
- "Connection": "keep-alive",
60
- "Content-Type": "text/event-stream",
61
- },
62
- )
63
-
64
-
65
- @app.post("/abort")
66
- async def abort(request: Request):
67
- data = await request.json()
68
- session_id = data.get("session_id")
69
- if session_id in sessions:
70
- scraper = sessions[session_id]["scraper"]
71
- await scraper.close()
72
- del sessions[session_id]
73
- return {"status": "aborted"}
74
-
75
-
76
- if __name__ == "__main__":
77
- logger.info("Starting KnowledgeNet server...")
78
- import uvicorn
79
-
80
- uvicorn.run(app, host="127.0.0.1", port=5000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
langgraph_backend/tools_tools.py CHANGED
@@ -13,7 +13,7 @@ from scraper import CrawlForAIScraper
13
 
14
  load_dotenv()
15
  scraper_inst = CrawlForAIScraper()
16
- model = ChatGoogleGenerativeAI(model="gemini-2.0-flash-lite", google_api_key=os.getenv("GOOGLE_API_KEY"))
17
 
18
 
19
  @tool
@@ -41,15 +41,15 @@ async def search(query: str) -> str:
41
  return "\n\n---\n\n".join(summ_sites_ctx) + "\n\nPlease call the search tool to get more information."
42
 
43
 
44
- def gen_report(findings: str, topic: str):
45
  # Generate report outline
46
- outline = model.with_structured_output(ReportOutline).invoke(REPORT_OUTLINE_PROMPT.format(topic=topic, ctx_manager=findings))
47
  report = []
48
  raster_report = f"# {outline['title']}\n\n"
49
 
50
  # Fill in report outline
51
  for i, heading in enumerate(outline["headings"]):
52
- content = model.with_structured_output(ReportFillin).invoke(
53
  REPORT_FILLIN_PROMPT.format(
54
  topic=topic,
55
  ctx_manager=findings,
@@ -57,7 +57,8 @@ def gen_report(findings: str, topic: str):
57
  report_outline=["[done] " + outline["title"]] + [f"[done] {h}" for _, h in enumerate(outline["headings"]) if i < _],
58
  slot=heading,
59
  ),
60
- )["content"]
 
61
  # Remove heading if LLM put it there regardless
62
  idx_heading = content.find(heading)
63
  if idx_heading != -1:
 
13
 
14
  load_dotenv()
15
  scraper_inst = CrawlForAIScraper()
16
+ model = ChatGoogleGenerativeAI(model="gemini-flash-lite-latest", google_api_key=os.getenv("GOOGLE_API_KEY"))
17
 
18
 
19
  @tool
 
41
  return "\n\n---\n\n".join(summ_sites_ctx) + "\n\nPlease call the search tool to get more information."
42
 
43
 
44
+ async def gen_report(findings: str, topic: str):
45
  # Generate report outline
46
+ outline = await model.with_structured_output(ReportOutline).ainvoke(REPORT_OUTLINE_PROMPT.format(topic=topic, ctx_manager=findings))
47
  report = []
48
  raster_report = f"# {outline['title']}\n\n"
49
 
50
  # Fill in report outline
51
  for i, heading in enumerate(outline["headings"]):
52
+ content = await model.with_structured_output(ReportFillin).ainvoke(
53
  REPORT_FILLIN_PROMPT.format(
54
  topic=topic,
55
  ctx_manager=findings,
 
57
  report_outline=["[done] " + outline["title"]] + [f"[done] {h}" for _, h in enumerate(outline["headings"]) if i < _],
58
  slot=heading,
59
  ),
60
+ )
61
+ content = content["content"]
62
  # Remove heading if LLM put it there regardless
63
  idx_heading = content.find(heading)
64
  if idx_heading != -1:
langgraph_backend/uv.lock CHANGED
@@ -1,5 +1,5 @@
1
  version = 1
2
- revision = 2
3
  requires-python = ">=3.11"
4
  resolution-markers = [
5
  "python_full_version >= '3.13'",
@@ -1033,10 +1033,10 @@ name = "langgraph"
1033
  version = "0.4.3"
1034
  source = { registry = "https://pypi.org/simple" }
1035
  dependencies = [
1036
- { name = "langchain-core", marker = "python_full_version < '4.0'" },
1037
  { name = "langgraph-checkpoint" },
1038
- { name = "langgraph-prebuilt", marker = "python_full_version < '4.0'" },
1039
- { name = "langgraph-sdk", marker = "python_full_version < '4.0'" },
1040
  { name = "pydantic" },
1041
  { name = "xxhash" },
1042
  ]
 
1
  version = 1
2
+ revision = 3
3
  requires-python = ">=3.11"
4
  resolution-markers = [
5
  "python_full_version >= '3.13'",
 
1033
  version = "0.4.3"
1034
  source = { registry = "https://pypi.org/simple" }
1035
  dependencies = [
1036
+ { name = "langchain-core", marker = "python_full_version < '4'" },
1037
  { name = "langgraph-checkpoint" },
1038
+ { name = "langgraph-prebuilt", marker = "python_full_version < '4'" },
1039
+ { name = "langgraph-sdk", marker = "python_full_version < '4'" },
1040
  { name = "pydantic" },
1041
  { name = "xxhash" },
1042
  ]