Compare commits

...

29 Commits

Author SHA1 Message Date
dependabot[bot]
acfa972c40 chore(deps): bump actions/setup-python from 5 to 6
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 09:17:48 +00:00
Manish Madan
3ceabed8ad Merge pull request #2406 from arc53/dependabot/npm_and_yarn/extensions/react-widget/npm_and_yarn-2a73d1bbcf
chore(deps): bump dompurify from 3.3.3 to 3.4.0 in /extensions/react-widget in the npm_and_yarn group across 1 directory
2026-04-20 14:45:32 +05:30
dependabot[bot]
422a4b139e chore(deps): bump dompurify
Bumps the npm_and_yarn group with 1 update in the /extensions/react-widget directory: [dompurify](https://github.com/cure53/DOMPurify).


Updates `dompurify` from 3.3.3 to 3.4.0
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.3.3...3.4.0)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 08:58:53 +00:00
Manish Madan
e85935eed0 Merge pull request #2402 from arc53/dependabot/npm_and_yarn/extensions/react-widget/flow-bin-0.309.0
chore(deps): bump flow-bin from 0.306.0 to 0.309.0 in /extensions/react-widget
2026-04-20 14:27:30 +05:30
dependabot[bot]
6a69b8aca0 chore(deps): bump flow-bin in /extensions/react-widget
Bumps [flow-bin](https://github.com/flowtype/flow-bin) from 0.306.0 to 0.309.0.
- [Release notes](https://github.com/flowtype/flow-bin/releases)
- [Commits](https://github.com/flowtype/flow-bin/commits)

---
updated-dependencies:
- dependency-name: flow-bin
  dependency-version: 0.309.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 08:55:07 +00:00
Manish Madan
33c2cc9660 Merge pull request #2400 from arc53/dependabot/npm_and_yarn/extensions/react-widget/styled-components-6.4.0
chore(deps): bump styled-components from 6.3.12 to 6.4.0 in /extensions/react-widget
2026-04-20 13:30:30 +05:30
dependabot[bot]
175d4d5a68 chore(deps): bump styled-components in /extensions/react-widget
Bumps [styled-components](https://github.com/styled-components/styled-components) from 6.3.12 to 6.4.0.
- [Release notes](https://github.com/styled-components/styled-components/releases)
- [Commits](https://github.com/styled-components/styled-components/compare/styled-components@6.3.12...styled-components@6.4.0)

---
updated-dependencies:
- dependency-name: styled-components
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 07:59:02 +00:00
Manish Madan
6c3ead1071 Merge pull request #2413 from arc53/dependabot/npm_and_yarn/docs/npm_and_yarn-a087653e68
chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates
2026-04-20 00:53:14 +05:30
dependabot[bot]
d23f88f825 chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates
Bumps the npm_and_yarn group with 2 updates in the /docs directory: [@xmldom/xmldom](https://github.com/xmldom/xmldom) and [lodash-es](https://github.com/lodash/lodash).


Updates `@xmldom/xmldom` from 0.9.8 to 0.9.9
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.9.8...0.9.9)

Updates `lodash-es` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-version: 0.9.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 17:55:14 +00:00
Manish Madan
da1df515f7 Merge pull request #2411 from arc53/dependabot/npm_and_yarn/docs/npm_and_yarn-bfabbafa3d
chore(deps): bump the npm_and_yarn group across 3 directories with 10 updates
2026-04-19 23:23:34 +05:30
dependabot[bot]
671a9d75ad chore(deps): bump the npm_and_yarn group across 3 directories with 10 updates
Bumps the npm_and_yarn group with 6 updates in the /docs directory:

| Package | From | To |
| --- | --- | --- |
| [next](https://github.com/vercel/next.js) | `15.5.9` | `15.5.15` |
| [brace-expansion](https://github.com/juliangruber/brace-expansion) | `5.0.2` | `5.0.5` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.3.1` | `3.4.0` |
| [markdown-it](https://github.com/markdown-it/markdown-it) | `14.1.0` | `14.1.1` |
| [minimatch](https://github.com/isaacs/minimatch) | `10.2.1` | `10.2.5` |
| [yaml](https://github.com/eemeli/yaml) | `1.10.2` | `1.10.3` |

Bumps the npm_and_yarn group with 2 updates in the /extensions/chrome directory: [yaml](https://github.com/eemeli/yaml) and [picomatch](https://github.com/micromatch/picomatch).
Bumps the npm_and_yarn group with 4 updates in the /extensions/web-widget directory: [brace-expansion](https://github.com/juliangruber/brace-expansion), [minimatch](https://github.com/isaacs/minimatch), [yaml](https://github.com/eemeli/yaml) and [picomatch](https://github.com/micromatch/picomatch).


Updates `next` from 15.5.9 to 15.5.15
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.5.9...v15.5.15)

Updates `brace-expansion` from 5.0.2 to 5.0.5
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.2...v5.0.5)

Updates `dompurify` from 3.3.1 to 3.4.0
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.3.1...3.4.0)

Updates `markdown-it` from 14.1.0 to 14.1.1
- [Changelog](https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md)
- [Commits](https://github.com/markdown-it/markdown-it/compare/14.1.0...14.1.1)

Updates `minimatch` from 10.2.1 to 10.2.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.1...v10.2.5)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `diff` from 5.2.0 to 5.2.2
- [Changelog](https://github.com/kpdecker/jsdiff/blob/master/release-notes.md)
- [Commits](https://github.com/kpdecker/jsdiff/compare/v5.2.0...v5.2.2)

Updates `glob` from 10.3.12 to 10.5.0
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.12...v10.5.0)

Updates `tar` from 6.2.1 to 7.5.11
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.1...v7.5.11)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `brace-expansion` from 2.0.1 to 2.0.2
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.2...v5.0.5)

Updates `glob` from 10.3.12 to 10.5.0
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.12...v10.5.0)

Updates `minimatch` from 9.0.4 to 9.0.9
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.1...v10.2.5)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `brace-expansion` from 1.1.11 to 1.1.14
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.2...v5.0.5)

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.1...v10.2.5)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `brace-expansion` from 1.1.11 to 1.1.14
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.2...v5.0.5)

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.1...v10.2.5)

Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.5.15
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 5.0.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: dompurify
  dependency-version: 3.4.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: markdown-it
  dependency-version: 14.1.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 10.2.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: diff
  dependency-version: 5.2.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: glob
  dependency-version: 10.5.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tar
  dependency-version: 7.5.11
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 2.0.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: glob
  dependency-version: 10.5.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 9.0.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.14
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.14
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 17:47:08 +00:00
Manish Madan
1c829667ff Merge pull request #2326 from arc53/dependabot/npm_and_yarn/extensions/react-widget/class-variance-authority-0.7.1
chore(deps): bump class-variance-authority from 0.7.0 to 0.7.1 in /extensions/react-widget
2026-04-19 23:13:02 +05:30
Manish Madan
3ab0ebb16d Merge pull request #2398 from arc53/dependabot/pip/application/openai-2.31.0
chore(deps): bump openai from 2.30.0 to 2.31.0 in /application
2026-04-19 23:10:36 +05:30
dependabot[bot]
988c4a5a15 chore(deps): bump openai from 2.30.0 to 2.31.0 in /application
Bumps [openai](https://github.com/openai/openai-python) from 2.30.0 to 2.31.0.
- [Release notes](https://github.com/openai/openai-python/releases)
- [Changelog](https://github.com/openai/openai-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-python/compare/v2.30.0...v2.31.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 2.31.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 17:14:43 +00:00
Manish Madan
01db8b2c41 Merge pull request #2395 from arc53/dependabot/pip/application/google-genai-1.73.1
chore(deps): bump google-genai from 1.69.0 to 1.73.1 in /application
2026-04-19 22:43:14 +05:30
Alex
ef19da9516 fix: pg bouncer compatible 2026-04-19 17:53:22 +01:00
dependabot[bot]
cc1275c3f9 chore(deps): bump google-genai from 1.69.0 to 1.73.1 in /application
Bumps [google-genai](https://github.com/googleapis/python-genai) from 1.69.0 to 1.73.1.
- [Release notes](https://github.com/googleapis/python-genai/releases)
- [Changelog](https://github.com/googleapis/python-genai/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-genai/compare/v1.69.0...v1.73.1)

---
updated-dependencies:
- dependency-name: google-genai
  dependency-version: 1.73.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 15:30:12 +00:00
Manish Madan
14c2f4890f Merge pull request #2394 from arc53/dependabot/pip/application/fastmcp-3.2.4
chore(deps): bump fastmcp from 3.2.0 to 3.2.4 in /application
2026-04-19 20:59:04 +05:30
dependabot[bot]
b3aec36aa2 chore(deps): bump fastmcp from 3.2.0 to 3.2.4 in /application
Bumps [fastmcp](https://github.com/PrefectHQ/fastmcp) from 3.2.0 to 3.2.4.
- [Release notes](https://github.com/PrefectHQ/fastmcp/releases)
- [Changelog](https://github.com/PrefectHQ/fastmcp/blob/main/docs/changelog.mdx)
- [Commits](https://github.com/PrefectHQ/fastmcp/compare/v3.2.0...v3.2.4)

---
updated-dependencies:
- dependency-name: fastmcp
  dependency-version: 3.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 15:13:45 +00:00
Manish Madan
50f62beaeb Merge pull request #2392 from arc53/dependabot/pip/application/elevenlabs-2.43.0
chore(deps): bump elevenlabs from 2.41.0 to 2.43.0 in /application
2026-04-19 20:42:32 +05:30
dependabot[bot]
423b4c6494 chore(deps): bump elevenlabs from 2.41.0 to 2.43.0 in /application
Bumps [elevenlabs](https://github.com/elevenlabs/elevenlabs-python) from 2.41.0 to 2.43.0.
- [Release notes](https://github.com/elevenlabs/elevenlabs-python/releases)
- [Commits](https://github.com/elevenlabs/elevenlabs-python/compare/v2.41.0...v2.43.0)

---
updated-dependencies:
- dependency-name: elevenlabs
  dependency-version: 2.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 14:49:32 +00:00
Manish Madan
54f615c59d Merge pull request #2407 from arc53/dependabot/npm_and_yarn/frontend/npm_and_yarn-f0540bac9f
chore(deps): bump the npm_and_yarn group across 1 directory with 4 updates
2026-04-19 20:17:07 +05:30
dependabot[bot]
223b3de66e chore(deps): bump the npm_and_yarn group across 1 directory with 4 updates
Bumps the npm_and_yarn group with 3 updates in the /frontend directory: [lodash](https://github.com/lodash/lodash), [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and [dompurify](https://github.com/cure53/DOMPurify).


Updates `lodash` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

Updates `vite` from 8.0.0 to 8.0.8
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.8/packages/vite)

Updates `picomatch` from 4.0.3 to 4.0.4
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/4.0.3...4.0.4)

Updates `dompurify` from 3.3.3 to 3.4.0
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.3.3...3.4.0)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 8.0.8
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version: 4.0.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: dompurify
  dependency-version: 3.4.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 14:06:01 +00:00
Manish Madan
4db9622ef5 Merge pull request #2403 from arc53/dependabot/npm_and_yarn/frontend/types/react-19.2.14
chore(deps-dev): bump @types/react from 19.2.2 to 19.2.14 in /frontend
2026-04-19 19:34:04 +05:30
dependabot[bot]
e8d1bbfb68 chore(deps-dev): bump @types/react from 19.2.2 to 19.2.14 in /frontend
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.2.2 to 19.2.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-19 13:54:47 +00:00
Manish Madan
aff1345ae4 Merge pull request #2399 from arc53/dependabot/npm_and_yarn/frontend/react-router-dom-7.14.1
chore(deps): bump react-router-dom from 7.13.1 to 7.14.1 in /frontend
2026-04-19 04:45:57 +05:30
Alex
ee430aff1e fix: tests 2026-04-18 13:28:03 +01:00
dependabot[bot]
ebb7938d1b chore(deps): bump react-router-dom from 7.13.1 to 7.14.1 in /frontend
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 7.13.1 to 7.14.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.14.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-version: 7.14.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 08:55:15 +00:00
dependabot[bot]
cdb71a54f0 chore(deps): bump class-variance-authority in /extensions/react-widget
Bumps [class-variance-authority](https://github.com/joe-bell/cva) from 0.7.0 to 0.7.1.
- [Release notes](https://github.com/joe-bell/cva/releases)
- [Commits](https://github.com/joe-bell/cva/compare/v0.7.0...v0.7.1)

---
updated-dependencies:
- dependency-name: class-variance-authority
  dependency-version: 0.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-24 20:53:18 +00:00
18 changed files with 2023 additions and 2935 deletions

View File

@@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install dependencies

View File

@@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@@ -13,12 +13,12 @@ onnxruntime>=1.19.0
docx2txt==0.9
ddgs>=8.0.0
fast-ebook
elevenlabs==2.41.0
elevenlabs==2.43.0
Flask==3.1.3
faiss-cpu==1.13.2
fastmcp==3.2.0
fastmcp==3.2.4
flask-restx==1.3.2
google-genai==1.69.0
google-genai==1.73.1
google-api-python-client==2.193.0
google-auth-httplib2==0.3.1
google-auth-oauthlib==1.3.1
@@ -47,7 +47,7 @@ msal==1.35.1
mypy-extensions==1.1.0
networkx==3.6.1
numpy==2.4.4
openai==2.30.0
openai==2.32.0
openapi3-parser==1.1.22
orjson==3.11.7
packaging==26.0

View File

@@ -16,7 +16,7 @@ don't need to know about SQLAlchemy dialect prefixes.
from typing import Optional
from sqlalchemy import Engine, create_engine
from sqlalchemy import Engine, create_engine, event
from application.core.settings import settings
@@ -43,8 +43,7 @@ def _resolve_uri() -> str:
#: Per-statement wall-clock cap applied to every connection handed out by
#: the engine. 30s is generous for interactive hot paths (reads under a few
#: hundred ms are normal) but still catches a runaway query before it
#: stacks up on PgBouncer or holds locks indefinitely. Override by
#: rebuilding the engine with a different ``connect_args`` in tests.
#: stacks up on PgBouncer or holds locks indefinitely.
STATEMENT_TIMEOUT_MS = 30_000
@@ -52,8 +51,9 @@ def get_engine() -> Engine:
"""Return the process-wide SQLAlchemy Engine, creating it if needed.
The engine applies a server-side ``statement_timeout`` to every
connection it hands out, so both :func:`db_session` and
:func:`db_readonly` inherit the same guardrail.
connection it hands out via a ``connect`` event, so both
:func:`db_session` and :func:`db_readonly` inherit the same
guardrail.
Returns:
A SQLAlchemy ``Engine`` configured with a pooled connection to
@@ -68,13 +68,20 @@ def get_engine() -> Engine:
pool_pre_ping=True, # survive PgBouncer / idle-disconnect recycles
pool_recycle=1800,
future=True,
connect_args={
# ``-c`` passes a GUC to the backend at connect time. This
# covers *all* sessions — interactive, Celery, seeder — so
# no route-handler can opt out by accident.
"options": f"-c statement_timeout={STATEMENT_TIMEOUT_MS}",
},
)
@event.listens_for(_engine, "connect")
def _apply_session_guardrails(dbapi_conn, _record):
# Apply as a SQL ``SET`` (not a libpq ``options=-c ...``
# startup parameter) so the engine works behind
# PgBouncer-style poolers — notably Neon's ``-pooler``
# endpoint, which rejects startup options. Explicit
# ``commit()`` so the session-level SET survives SA's
# transaction resets on pool return.
with dbapi_conn.cursor() as cur:
cur.execute(f"SET statement_timeout = {STATEMENT_TIMEOUT_MS}")
dbapi_conn.commit()
return _engine

3063
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"dependencies": {
"@vercel/analytics": "^1.1.1",
"docsgpt-react": "^0.5.1",
"next": "^15.5.9",
"next": "^15.5.15",
"nextra": "^4.6.1",
"nextra-theme-docs": "^4.6.1",
"react": "^18.2.0",

View File

@@ -458,10 +458,11 @@
"dev": true
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
@@ -802,10 +803,11 @@
}
},
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">= 6"
}
@@ -1137,9 +1139,9 @@
"dev": true
},
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true
},
"pify": {
@@ -1335,9 +1337,9 @@
"dev": true
},
"yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"dev": true
}
}

View File

@@ -19,7 +19,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"dompurify": "^3.1.5",
"flow-bin": "^0.306.0",
"flow-bin": "^0.309.0",
"markdown-it": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -1697,12 +1697,6 @@
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
"license": "MIT"
},
"node_modules/@emotion/unitless": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
"license": "MIT"
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
@@ -4542,12 +4536,6 @@
"@types/react": "*"
}
},
"node_modules/@types/stylis": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz",
"integrity": "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==",
"license": "MIT"
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -5311,22 +5299,15 @@
}
},
"node_modules/class-variance-authority": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz",
"integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==",
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
"license": "Apache-2.0",
"dependencies": {
"clsx": "2.0.0"
"clsx": "^2.1.1"
},
"funding": {
"url": "https://joebell.co.uk"
}
},
"node_modules/class-variance-authority/node_modules/clsx": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
"engines": {
"node": ">=6"
"url": "https://polar.sh/cva"
}
},
"node_modules/clone": {
@@ -5727,9 +5708,9 @@
}
},
"node_modules/dompurify": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz",
"integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz",
"integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -6508,9 +6489,9 @@
"license": "ISC"
},
"node_modules/flow-bin": {
"version": "0.306.0",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.306.0.tgz",
"integrity": "sha512-NaAyPsFWZSY59NLCL+6lUXPS5KCO4td9h7XO0kV9VGzRr19sypImQYc0DD1Skm+SHt0/mOIYKI21KtmhQ4KHBg==",
"version": "0.309.0",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.309.0.tgz",
"integrity": "sha512-/RH68gcCY8OHzcdSVTUCw+fhDSEYmNHoovfK0EcbB4rs1Xbc5HhxhHTvr7U+h55De4bDRlE52ghH23MRP625cQ==",
"license": "MIT",
"bin": {
"flow": "cli.js"
@@ -7889,24 +7870,6 @@
"node-gyp-build-optional-packages-test": "build-test.js"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -8355,34 +8318,6 @@
"node": ">= 0.4"
}
},
"node_modules/postcss": {
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
@@ -8796,11 +8731,6 @@
"node": ">= 0.4"
}
},
"node_modules/shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -8914,6 +8844,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -9045,20 +8976,15 @@
}
},
"node_modules/styled-components": {
"version": "6.3.12",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.3.12.tgz",
"integrity": "sha512-hFR6xsVkVYbsdcUlzPYFvFfoc6o2KlV0VvgRIQwSYMtdThM7SCxnjX9efh/cWce2kTq16I/Kl3xM98xiLptsXA==",
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.4.0.tgz",
"integrity": "sha512-BL1EDFpt+q10eAeZB0q9ps6pSlPejaBQWBkiuM16pyoVTG4NhZrPrZK0cqNbrozxSsYwUsJ9SQYN6NyeKJYX9A==",
"license": "MIT",
"dependencies": {
"@emotion/is-prop-valid": "1.4.0",
"@emotion/unitless": "0.10.0",
"@types/stylis": "4.2.7",
"css-to-react-native": "3.2.0",
"csstype": "3.2.3",
"postcss": "8.4.49",
"shallowequal": "1.1.0",
"stylis": "4.3.6",
"tslib": "2.8.1"
"stylis": "4.3.6"
},
"engines": {
"node": ">= 16"
@@ -9068,12 +8994,20 @@
"url": "https://opencollective.com/styled-components"
},
"peerDependencies": {
"css-to-react-native": ">= 3.2.0",
"react": ">= 16.8.0",
"react-dom": ">= 16.8.0"
"react-dom": ">= 16.8.0",
"react-native": ">= 0.68.0"
},
"peerDependenciesMeta": {
"css-to-react-native": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},

View File

@@ -52,7 +52,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"dompurify": "^3.1.5",
"flow-bin": "^0.306.0",
"flow-bin": "^0.309.0",
"markdown-it": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -142,10 +142,11 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -492,10 +493,11 @@
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -590,10 +592,11 @@
"dev": true
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
@@ -990,10 +993,11 @@
"dev": true
},
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">= 6"
}

View File

@@ -20,7 +20,7 @@
"copy-to-clipboard": "^3.3.3",
"i18next": "^26.0.4",
"i18next-browser-languagedetector": "^8.2.1",
"lodash": "^4.17.21",
"lodash": "^4.18.1",
"lucide-react": "^1.8.0",
"mermaid": "^11.14.0",
"prop-types": "^15.8.1",
@@ -33,7 +33,7 @@
"react-i18next": "^17.0.2",
"react-markdown": "^9.0.1",
"react-redux": "^9.2.0",
"react-router-dom": "^7.6.1",
"react-router-dom": "^7.14.1",
"react-syntax-highlighter": "^16.1.1",
"reactflow": "^11.11.4",
"rehype-katex": "^7.0.1",
@@ -44,7 +44,7 @@
"devDependencies": {
"@tailwindcss/postcss": "^4.2.2",
"@types/lodash": "^4.17.20",
"@types/react": "^19.1.8",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/react-syntax-highlighter": "^15.5.13",
"@typescript-eslint/eslint-plugin": "^8.58.2",
@@ -66,7 +66,7 @@
"tailwindcss": "^4.2.2",
"tw-animate-css": "^1.4.0",
"typescript": "^5.8.3",
"vite": "^8.0.0",
"vite": "^8.0.8",
"vite-plugin-svgr": "^4.3.0"
}
},
@@ -418,21 +418,21 @@
"license": "Apache-2.0"
},
"node_modules/@emnapi/core": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz",
"integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==",
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
"integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.0",
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",
"integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
"integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -441,9 +441,9 @@
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
"integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -772,36 +772,28 @@
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
"integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1",
"@tybys/wasm-util": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@oxc-project/runtime": {
"version": "0.115.0",
"resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz",
"integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1"
}
},
"node_modules/@oxc-project/types": {
"version": "0.115.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz",
"integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==",
"version": "0.124.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
"integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==",
"dev": true,
"license": "MIT",
"funding": {
@@ -2592,9 +2584,9 @@
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz",
"integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz",
"integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==",
"cpu": [
"arm64"
],
@@ -2609,9 +2601,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz",
"integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz",
"integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==",
"cpu": [
"arm64"
],
@@ -2626,9 +2618,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz",
"integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz",
"integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==",
"cpu": [
"x64"
],
@@ -2643,9 +2635,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz",
"integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz",
"integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==",
"cpu": [
"x64"
],
@@ -2660,9 +2652,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz",
"integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz",
"integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==",
"cpu": [
"arm"
],
@@ -2677,9 +2669,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==",
"cpu": [
"arm64"
],
@@ -2694,9 +2686,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz",
"integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz",
"integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==",
"cpu": [
"arm64"
],
@@ -2711,9 +2703,9 @@
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==",
"cpu": [
"ppc64"
],
@@ -2728,9 +2720,9 @@
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==",
"cpu": [
"s390x"
],
@@ -2745,9 +2737,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz",
"integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==",
"cpu": [
"x64"
],
@@ -2762,9 +2754,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz",
"integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz",
"integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==",
"cpu": [
"x64"
],
@@ -2779,9 +2771,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz",
"integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz",
"integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==",
"cpu": [
"arm64"
],
@@ -2796,9 +2788,9 @@
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz",
"integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz",
"integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==",
"cpu": [
"wasm32"
],
@@ -2806,16 +2798,18 @@
"license": "MIT",
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^1.1.1"
"@emnapi/core": "1.9.2",
"@emnapi/runtime": "1.9.2",
"@napi-rs/wasm-runtime": "^1.1.3"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz",
"integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz",
"integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==",
"cpu": [
"arm64"
],
@@ -2830,9 +2824,9 @@
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz",
"integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz",
"integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==",
"cpu": [
"x64"
],
@@ -2876,19 +2870,6 @@
}
}
},
"node_modules/@rollup/pluginutils/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -3833,12 +3814,12 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.2.2",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
@@ -4981,9 +4962,9 @@
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/cytoscape": {
@@ -5675,9 +5656,9 @@
}
},
"node_modules/dompurify": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz",
"integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz",
"integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -8317,19 +8298,6 @@
"url": "https://opencollective.com/lint-staged"
}
},
"node_modules/lint-staged/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/listr2": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
@@ -8382,9 +8350,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash-es": {
@@ -9932,6 +9900,19 @@
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pkg-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
@@ -10456,9 +10437,9 @@
}
},
"node_modules/react-router": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz",
"integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.1.tgz",
"integrity": "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -10478,12 +10459,12 @@
}
},
"node_modules/react-router-dom": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz",
"integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==",
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.1.tgz",
"integrity": "sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA==",
"license": "MIT",
"dependencies": {
"react-router": "7.13.1"
"react-router": "7.14.1"
},
"engines": {
"node": ">=20.0.0"
@@ -10807,14 +10788,14 @@
"license": "Unlicense"
},
"node_modules/rolldown": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz",
"integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz",
"integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.115.0",
"@rolldown/pluginutils": "1.0.0-rc.9"
"@oxc-project/types": "=0.124.0",
"@rolldown/pluginutils": "1.0.0-rc.15"
},
"bin": {
"rolldown": "bin/cli.mjs"
@@ -10823,27 +10804,27 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.0-rc.9",
"@rolldown/binding-darwin-arm64": "1.0.0-rc.9",
"@rolldown/binding-darwin-x64": "1.0.0-rc.9",
"@rolldown/binding-freebsd-x64": "1.0.0-rc.9",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9",
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.9",
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.9",
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.9",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9",
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9"
"@rolldown/binding-android-arm64": "1.0.0-rc.15",
"@rolldown/binding-darwin-arm64": "1.0.0-rc.15",
"@rolldown/binding-darwin-x64": "1.0.0-rc.15",
"@rolldown/binding-freebsd-x64": "1.0.0-rc.15",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15",
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15",
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.15",
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.15",
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.15",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15",
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
}
},
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz",
"integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==",
"version": "1.0.0-rc.15",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz",
"integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==",
"dev": true,
"license": "MIT"
},
@@ -11513,19 +11494,6 @@
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
@@ -11588,19 +11556,6 @@
"typescript": ">=4.0.0"
}
},
"node_modules/ts-declaration-location/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/ts-dedent": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
@@ -12046,17 +12001,16 @@
}
},
"node_modules/vite": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz",
"integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==",
"version": "8.0.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz",
"integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/runtime": "0.115.0",
"lightningcss": "^1.32.0",
"picomatch": "^4.0.3",
"picomatch": "^4.0.4",
"postcss": "^8.5.8",
"rolldown": "1.0.0-rc.9",
"rolldown": "1.0.0-rc.15",
"tinyglobby": "^0.2.15"
},
"bin": {
@@ -12073,8 +12027,8 @@
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"@vitejs/devtools": "^0.0.0-alpha.31",
"esbuild": "^0.27.0",
"@vitejs/devtools": "^0.1.0",
"esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"sass": "^1.70.0",
@@ -12139,19 +12093,6 @@
"vite": ">=2.6.0"
}
},
"node_modules/vite/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",

View File

@@ -31,7 +31,7 @@
"copy-to-clipboard": "^3.3.3",
"i18next": "^26.0.4",
"i18next-browser-languagedetector": "^8.2.1",
"lodash": "^4.17.21",
"lodash": "^4.18.1",
"lucide-react": "^1.8.0",
"mermaid": "^11.14.0",
"prop-types": "^15.8.1",
@@ -44,7 +44,7 @@
"react-i18next": "^17.0.2",
"react-markdown": "^9.0.1",
"react-redux": "^9.2.0",
"react-router-dom": "^7.6.1",
"react-router-dom": "^7.14.1",
"react-syntax-highlighter": "^16.1.1",
"reactflow": "^11.11.4",
"rehype-katex": "^7.0.1",
@@ -55,7 +55,7 @@
"devDependencies": {
"@tailwindcss/postcss": "^4.2.2",
"@types/lodash": "^4.17.20",
"@types/react": "^19.1.8",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/react-syntax-highlighter": "^15.5.13",
"@typescript-eslint/eslint-plugin": "^8.58.2",
@@ -77,7 +77,7 @@
"tailwindcss": "^4.2.2",
"tw-animate-css": "^1.4.0",
"typescript": "^5.8.3",
"vite": "^8.0.0",
"vite": "^8.0.8",
"vite-plugin-svgr": "^4.3.0"
}
}

View File

@@ -1,164 +0,0 @@
"""Integration tests for ``_backfill_connector_sessions``.
Mongo routinely contains multiple rows for the same
``(user_id, server_url, provider)`` triple — each OAuth-button click
inserts a pending row and only the last one is authorized. The Postgres
schema has a unique index on that triple, so the backfill does a Python-
side dedup *before* the insert, preferring authorized rows over pending
ones and newer ``created_at`` over older.
This test seeds exactly that shape and asserts the dedup keeps the
authorized row.
"""
from __future__ import annotations
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import mongomock
import pytest
from sqlalchemy import text
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
from scripts.db.backfill import _backfill_connector_sessions # noqa: E402
@pytest.fixture
def mongo_db() -> Any:
client = mongomock.MongoClient()
return client["docsgpt_test"]
class TestBackfillConnectorSessions:
def test_dedups_authorized_over_pending(self, pg_conn, mongo_db):
# Two rows, same (user, server_url, provider) triple. The pending
# row was inserted first; the authorized row was inserted later
# when the OAuth redirect completed. Dedup should keep the
# authorized one (has ``token_info``).
mongo_db["connector_sessions"].insert_many(
[
{
"_id": "777777777777777777777771",
"user_id": "alice",
"provider": "google_drive",
"server_url": "https://drive.google.com",
"status": "pending",
"created_at": datetime(2026, 1, 1, tzinfo=timezone.utc),
},
{
"_id": "777777777777777777777772",
"user_id": "alice",
"provider": "google_drive",
"server_url": "https://drive.google.com",
"status": "authorized",
"token_info": {"access_token": "xyz"},
"created_at": datetime(2026, 1, 2, tzinfo=timezone.utc),
},
]
)
stats = _backfill_connector_sessions(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 2
assert stats["written"] == 1
assert stats["skipped"] == 1
rows = pg_conn.execute(
text(
"SELECT status, legacy_mongo_id, token_info FROM "
"connector_sessions WHERE user_id = 'alice'"
)
).fetchall()
assert len(rows) == 1
assert rows[0]._mapping["status"] == "authorized"
assert rows[0]._mapping["legacy_mongo_id"] == "777777777777777777777772"
assert rows[0]._mapping["token_info"] == {"access_token": "xyz"}
def test_dedups_newer_created_at_when_both_pending(self, pg_conn, mongo_db):
# Both rows are pending. Newer created_at wins.
mongo_db["connector_sessions"].insert_many(
[
{
"_id": "777777777777777777777771",
"user_id": "alice",
"provider": "github",
"server_url": "",
"status": "pending",
"created_at": datetime(2026, 1, 1, tzinfo=timezone.utc),
},
{
"_id": "777777777777777777777772",
"user_id": "alice",
"provider": "github",
"server_url": "",
"status": "pending",
"created_at": datetime(2026, 2, 1, tzinfo=timezone.utc),
},
]
)
_backfill_connector_sessions(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
rows = pg_conn.execute(
text(
"SELECT legacy_mongo_id FROM connector_sessions "
"WHERE user_id = 'alice' AND provider = 'github'"
)
).fetchall()
assert len(rows) == 1
assert rows[0]._mapping["legacy_mongo_id"] == "777777777777777777777772"
def test_rerun_does_not_duplicate(self, pg_conn, mongo_db):
mongo_db["connector_sessions"].insert_one(
{
"_id": "777777777777777777777771",
"user_id": "alice",
"provider": "google_drive",
"server_url": "https://drive.google.com",
"status": "authorized",
"token_info": {"access_token": "xyz"},
"created_at": datetime(2026, 1, 1, tzinfo=timezone.utc),
}
)
_backfill_connector_sessions(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_connector_sessions(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text(
"SELECT count(*) FROM connector_sessions "
"WHERE legacy_mongo_id = '777777777777777777777771'"
)
).scalar()
assert count == 1
def test_skips_rows_without_user_or_provider(self, pg_conn, mongo_db):
mongo_db["connector_sessions"].insert_many(
[
{
"_id": "777777777777777777777771",
"provider": "github", # no user
},
{
"_id": "777777777777777777777772",
"user_id": "alice", # no provider
},
]
)
stats = _backfill_connector_sessions(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 2
assert stats["written"] == 0
assert stats["skipped"] == 2

View File

@@ -1,292 +0,0 @@
"""Integration tests for ``_backfill_conversations`` +
``_backfill_conversation_messages`` flattening and attachment FK
resolution.
The conversations backfill does two things the other backfillers don't:
1. It flattens Mongo's nested ``queries[]`` array into rows in a child
table (``conversation_messages``), with ``position`` = array index.
2. It resolves attachment refs (strings pointing to Mongo ObjectIds) to
Postgres UUIDs via a ``legacy_mongo_id`` lookup. Unresolvable refs are
dropped rather than crashing the whole batch.
These are the two bits we assert below, alongside the standard
happy-shape / idempotency / convergence checks.
"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Any
import mongomock
import pytest
from sqlalchemy import text
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
from scripts.db.backfill import ( # noqa: E402
_backfill_attachments,
_backfill_conversations,
)
@pytest.fixture
def mongo_db() -> Any:
client = mongomock.MongoClient()
return client["docsgpt_test"]
# ---------------------------------------------------------------------------
# attachments — prerequisite for conversations (DBRef→UUID via legacy map)
# ---------------------------------------------------------------------------
class TestBackfillAttachments:
def test_attachments_happy_shape_preserves_mime_and_size(
self, pg_conn, mongo_db
):
mongo_db["attachments"].insert_one(
{
"_id": "aaaaaaaaaaaaaaaaaaaaaaaa",
"user": "alice",
"filename": "report.pdf",
# Worker writes the blob path as ``path``; the PG column is
# ``upload_path``. If the backfill ever reads the wrong key,
# this test will fail with an empty string.
"path": "uploads/alice/report.pdf",
"mime_type": "application/pdf",
"size": 12345,
"content": "extracted text",
"token_count": 42,
}
)
_backfill_attachments(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
row = pg_conn.execute(
text(
"SELECT filename, upload_path, mime_type, size, "
"content, token_count, legacy_mongo_id "
"FROM attachments WHERE user_id = 'alice'"
)
).one()._mapping
assert row["filename"] == "report.pdf"
assert row["upload_path"] == "uploads/alice/report.pdf"
assert row["mime_type"] == "application/pdf"
assert row["size"] == 12345
assert row["content"] == "extracted text"
assert row["token_count"] == 42
assert row["legacy_mongo_id"] == "aaaaaaaaaaaaaaaaaaaaaaaa"
def test_attachments_rerun_does_not_duplicate(self, pg_conn, mongo_db):
mongo_db["attachments"].insert_one(
{
"_id": "aaaaaaaaaaaaaaaaaaaaaaaa",
"user": "alice",
"filename": "r.pdf",
"path": "u/alice/r.pdf",
}
)
_backfill_attachments(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_attachments(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text(
"SELECT count(*) FROM attachments "
"WHERE legacy_mongo_id = 'aaaaaaaaaaaaaaaaaaaaaaaa'"
)
).scalar()
assert count == 1
# ---------------------------------------------------------------------------
# conversations + conversation_messages
# ---------------------------------------------------------------------------
def _seed_attachment(mongo_db: Any, _id: str, user: str = "alice") -> None:
mongo_db["attachments"].insert_one(
{
"_id": _id,
"user": user,
"filename": f"{_id}.txt",
"path": f"uploads/{user}/{_id}.txt",
}
)
class TestBackfillConversations:
def test_conversations_flattens_queries_into_messages(
self, pg_conn, mongo_db
):
_seed_attachment(mongo_db, "aaaaaaaaaaaaaaaaaaaaaaa1")
_backfill_attachments(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["conversations"].insert_one(
{
"_id": "cccccccccccccccccccccccc",
"user": "alice",
"name": "chat-1",
"queries": [
{
"prompt": "hello",
"response": "hi",
"attachments": ["aaaaaaaaaaaaaaaaaaaaaaa1"],
},
{
"prompt": "how are you",
"response": "fine",
},
],
}
)
stats = _backfill_conversations(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 1
assert stats["written"] == 1
assert stats["messages_written"] == 2
conv_id = pg_conn.execute(
text(
"SELECT id FROM conversations "
"WHERE legacy_mongo_id = 'cccccccccccccccccccccccc'"
)
).scalar()
rows = pg_conn.execute(
text(
"SELECT position, prompt, response, attachments "
"FROM conversation_messages WHERE conversation_id = :cid "
"ORDER BY position"
),
{"cid": str(conv_id)},
).fetchall()
assert [r._mapping["position"] for r in rows] == [0, 1]
assert rows[0]._mapping["prompt"] == "hello"
assert rows[1]._mapping["response"] == "fine"
# Attachment ObjectId string was mapped to the PG UUID.
resolved = rows[0]._mapping["attachments"]
assert len(resolved) == 1
pg_att_id = pg_conn.execute(
text(
"SELECT id FROM attachments "
"WHERE legacy_mongo_id = 'aaaaaaaaaaaaaaaaaaaaaaa1'"
)
).scalar()
assert str(resolved[0]) == str(pg_att_id)
def test_conversations_drops_unresolved_attachments(
self, pg_conn, mongo_db
):
mongo_db["conversations"].insert_one(
{
"_id": "cccccccccccccccccccccccc",
"user": "alice",
"queries": [
{
"prompt": "hi",
# Unknown attachment objectid — not present in PG.
"attachments": ["bbbbbbbbbbbbbbbbbbbbbbb0"],
}
],
}
)
stats = _backfill_conversations(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["unresolved_attachment_refs"] == 1
row = pg_conn.execute(
text(
"SELECT attachments FROM conversation_messages "
"WHERE position = 0"
)
).one()._mapping
assert list(row["attachments"]) == []
def test_conversations_rerun_does_not_double_messages(
self, pg_conn, mongo_db
):
mongo_db["conversations"].insert_one(
{
"_id": "cccccccccccccccccccccccc",
"user": "alice",
"queries": [
{"prompt": "a", "response": "1"},
{"prompt": "b", "response": "2"},
],
}
)
_backfill_conversations(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_conversations(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
conv_count = pg_conn.execute(
text(
"SELECT count(*) FROM conversations "
"WHERE legacy_mongo_id = 'cccccccccccccccccccccccc'"
)
).scalar()
msg_count = pg_conn.execute(
text(
"SELECT count(*) FROM conversation_messages "
"WHERE conversation_id = ("
" SELECT id FROM conversations "
" WHERE legacy_mongo_id = 'cccccccccccccccccccccccc'"
")"
)
).scalar()
assert conv_count == 1
assert msg_count == 2
def test_conversations_rerun_truncates_removed_tail_messages(
self, pg_conn, mongo_db
):
# First run: 3 messages. Then Mongo truncates to 1. Second run
# should drop positions 1 and 2.
mongo_db["conversations"].insert_one(
{
"_id": "cccccccccccccccccccccccc",
"user": "alice",
"queries": [
{"prompt": "a", "response": "1"},
{"prompt": "b", "response": "2"},
{"prompt": "c", "response": "3"},
],
}
)
_backfill_conversations(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["conversations"].update_one(
{"_id": "cccccccccccccccccccccccc"},
{"$set": {"queries": [{"prompt": "a", "response": "1-updated"}]}},
)
_backfill_conversations(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
rows = pg_conn.execute(
text(
"SELECT position, response FROM conversation_messages "
"WHERE conversation_id = ("
" SELECT id FROM conversations "
" WHERE legacy_mongo_id = 'cccccccccccccccccccccccc'"
") ORDER BY position"
)
).fetchall()
assert len(rows) == 1
assert rows[0]._mapping["response"] == "1-updated"

View File

@@ -1,144 +0,0 @@
"""Integration tests for ``_backfill_sources``.
The notable translation here is the optional Mongo ``user`` field: system
seed rows arrive with no ``user`` (or ``user="system"``) and must land
with ``user_id = '__system__'`` in Postgres so the NOT NULL constraint
accepts them.
"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Any
import mongomock
import pytest
from sqlalchemy import text
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
from scripts.db.backfill import ( # noqa: E402
SYSTEM_USER_ID,
_backfill_sources,
)
@pytest.fixture
def mongo_db() -> Any:
client = mongomock.MongoClient()
return client["docsgpt_test"]
class TestBackfillSources:
def test_sources_system_rows_get_sentinel_user(self, pg_conn, mongo_db):
mongo_db["sources"].insert_many(
[
{
"_id": "555555555555555555555555",
"name": "Seed Source",
"type": "url",
# Intentionally no ``user`` field.
},
{
"_id": "666666666666666666666666",
"user": "alice",
"name": "Alice's Upload",
"type": "file",
},
]
)
stats = _backfill_sources(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 2
assert stats["written"] == 2
rows = pg_conn.execute(
text(
"SELECT user_id, name FROM sources "
"WHERE legacy_mongo_id IN "
"('555555555555555555555555', '666666666666666666666666') "
"ORDER BY name"
)
).fetchall()
assert len(rows) == 2
# Alphabetical by name: "Alice's Upload" comes before "Seed Source".
assert rows[0]._mapping["user_id"] == "alice"
assert rows[1]._mapping["user_id"] == SYSTEM_USER_ID
def test_sources_preserves_legacy_fields_under_metadata(
self, pg_conn, mongo_db
):
mongo_db["sources"].insert_one(
{
"_id": "555555555555555555555555",
"user": "alice",
"name": "s1",
"type": "url",
"status": "ingested", # legacy ingestion field
"reason": None,
}
)
_backfill_sources(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
meta = pg_conn.execute(
text(
"SELECT metadata FROM sources "
"WHERE legacy_mongo_id = '555555555555555555555555'"
)
).scalar()
assert meta["legacy_fields"]["status"] == "ingested"
def test_sources_rerun_does_not_duplicate(self, pg_conn, mongo_db):
mongo_db["sources"].insert_one(
{
"_id": "555555555555555555555555",
"user": "alice",
"name": "s1",
"type": "url",
}
)
_backfill_sources(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_sources(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text(
"SELECT count(*) FROM sources "
"WHERE legacy_mongo_id = '555555555555555555555555'"
)
).scalar()
assert count == 1
def test_sources_rerun_converges_on_name(self, pg_conn, mongo_db):
mongo_db["sources"].insert_one(
{
"_id": "555555555555555555555555",
"user": "alice",
"name": "old-name",
"type": "url",
}
)
_backfill_sources(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["sources"].update_one(
{"_id": "555555555555555555555555"},
{"$set": {"name": "new-name"}},
)
_backfill_sources(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
name = pg_conn.execute(
text(
"SELECT name FROM sources "
"WHERE legacy_mongo_id = '555555555555555555555555'"
)
).scalar()
assert name == "new-name"

View File

@@ -1,380 +0,0 @@
"""Integration tests for the per-tool-child collections: todos, notes,
and memories.
These all depend on ``user_tools`` having been backfilled first, because
:func:`scripts.db.backfill._build_tool_id_map` joins Mongo
``user_tools._id`` to Postgres ``user_tools.id`` via ``(user_id, name)``.
Each test seeds a single tool row to keep fixtures minimal.
Memories are the odd one out: the SQL uses ``ON CONFLICT DO NOTHING`` and
the table has no ``legacy_mongo_id`` column, so a re-run can't converge
on mutated content. The negative test locks in the current behavior so a
future fix (adding ``legacy_mongo_id`` + DO UPDATE, tracked in
``migration-postgres.md``) will flip the assertion.
"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Any
import mongomock
import pytest
from sqlalchemy import text
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
from scripts.db.backfill import ( # noqa: E402
_backfill_memories,
_backfill_notes,
_backfill_todos,
_backfill_user_tools,
)
_TOOL_MONGO_ID = "507f1f77bcf86cd799439011"
@pytest.fixture
def mongo_db() -> Any:
client = mongomock.MongoClient()
return client["docsgpt_test"]
@pytest.fixture
def seeded_tool(pg_conn, mongo_db) -> str:
"""Seed one tool in both Mongo and Postgres and return its PG UUID.
Having a user_tools row on both sides is a prerequisite for every
backfill function in this file — ``_build_tool_id_map`` needs it to
resolve ``tool_id`` FKs.
"""
mongo_db["user_tools"].insert_one(
{
"_id": _TOOL_MONGO_ID,
"user": "alice",
"name": "my-tool",
}
)
_backfill_user_tools(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
return str(
pg_conn.execute(
text("SELECT id FROM user_tools WHERE legacy_mongo_id = :lid"),
{"lid": _TOOL_MONGO_ID},
).scalar()
)
# ---------------------------------------------------------------------------
# todos
# ---------------------------------------------------------------------------
class TestBackfillTodos:
def test_todos_status_completed_translation(self, pg_conn, mongo_db, seeded_tool):
# Two todos: one "open", one "completed". Asserts the Mongo
# ``status`` string is translated to the PG ``completed`` bool.
mongo_db["todos"].insert_many(
[
{
"_id": "111111111111111111111111",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"todo_id": 1,
"title": "Write tests",
"status": "open",
},
{
"_id": "222222222222222222222222",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"todo_id": 2,
"title": "Ship it",
"status": "completed",
},
]
)
stats = _backfill_todos(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 2
assert stats["written"] == 2
rows = pg_conn.execute(
text(
"SELECT todo_id, title, completed, legacy_mongo_id FROM todos "
"WHERE user_id = 'alice' ORDER BY todo_id"
)
).fetchall()
assert len(rows) == 2
assert rows[0]._mapping["todo_id"] == 1
assert rows[0]._mapping["completed"] is False
assert rows[1]._mapping["todo_id"] == 2
assert rows[1]._mapping["completed"] is True
# legacy_mongo_id preserved for idempotency on re-run.
assert rows[0]._mapping["legacy_mongo_id"] == "111111111111111111111111"
def test_todos_legacy_fields_stashed_in_metadata(
self, pg_conn, mongo_db, seeded_tool
):
# Legacy top-level ``conversation_id`` field should survive under
# metadata.legacy_fields.
mongo_db["todos"].insert_one(
{
"_id": "111111111111111111111111",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"title": "t",
"status": "open",
"conversation_id": "abc-legacy",
}
)
_backfill_todos(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
meta = pg_conn.execute(
text("SELECT metadata FROM todos WHERE legacy_mongo_id = :lid"),
{"lid": "111111111111111111111111"},
).scalar()
assert meta["legacy_fields"]["conversation_id"] == "abc-legacy"
def test_todos_rerun_does_not_duplicate(self, pg_conn, mongo_db, seeded_tool):
mongo_db["todos"].insert_one(
{
"_id": "111111111111111111111111",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"todo_id": 1,
"title": "t",
"status": "open",
}
)
_backfill_todos(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_todos(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text(
"SELECT count(*) FROM todos "
"WHERE legacy_mongo_id = '111111111111111111111111'"
)
).scalar()
assert count == 1
def test_todos_rerun_converges_on_status_and_title(
self, pg_conn, mongo_db, seeded_tool
):
mongo_db["todos"].insert_one(
{
"_id": "111111111111111111111111",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"todo_id": 1,
"title": "Old title",
"status": "open",
}
)
_backfill_todos(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["todos"].update_one(
{"_id": "111111111111111111111111"},
{"$set": {"status": "completed", "title": "Done title"}},
)
_backfill_todos(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
row = pg_conn.execute(
text(
"SELECT title, completed FROM todos "
"WHERE legacy_mongo_id = '111111111111111111111111'"
)
).one()._mapping
assert row["title"] == "Done title"
assert row["completed"] is True
# ---------------------------------------------------------------------------
# notes
# ---------------------------------------------------------------------------
class TestBackfillNotes:
def test_notes_translates_note_to_content(
self, pg_conn, mongo_db, seeded_tool
):
mongo_db["notes"].insert_one(
{
"_id": "333333333333333333333333",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"note": "hello world",
# No explicit title — backfill should fall back to "note".
}
)
_backfill_notes(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
row = pg_conn.execute(
text(
"SELECT title, content, legacy_mongo_id FROM notes "
"WHERE user_id = 'alice'"
)
).one()._mapping
assert row["content"] == "hello world"
assert row["title"] == "note"
# legacy_mongo_id column landed recently — lock in its presence.
assert row["legacy_mongo_id"] == "333333333333333333333333"
def test_notes_uses_title_when_present(self, pg_conn, mongo_db, seeded_tool):
mongo_db["notes"].insert_one(
{
"_id": "333333333333333333333333",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"title": "My title",
"content": "direct content",
}
)
_backfill_notes(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
row = pg_conn.execute(
text("SELECT title, content FROM notes WHERE user_id = 'alice'")
).one()._mapping
assert row["title"] == "My title"
assert row["content"] == "direct content"
def test_notes_rerun_converges_on_content(
self, pg_conn, mongo_db, seeded_tool
):
mongo_db["notes"].insert_one(
{
"_id": "333333333333333333333333",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"note": "v1",
}
)
_backfill_notes(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["notes"].update_one(
{"_id": "333333333333333333333333"},
{"$set": {"note": "v2"}},
)
_backfill_notes(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
rows = pg_conn.execute(
text(
"SELECT content FROM notes "
"WHERE legacy_mongo_id = '333333333333333333333333'"
)
).fetchall()
assert len(rows) == 1
assert rows[0]._mapping["content"] == "v2"
# ---------------------------------------------------------------------------
# memories
# ---------------------------------------------------------------------------
class TestBackfillMemories:
def test_memories_happy_shape(self, pg_conn, mongo_db, seeded_tool):
mongo_db["memories"].insert_one(
{
"_id": "444444444444444444444444",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"path": "/foo",
"content": "memory body",
}
)
_backfill_memories(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
row = pg_conn.execute(
text("SELECT path, content FROM memories WHERE user_id = 'alice'")
).one()._mapping
assert row["path"] == "/foo"
assert row["content"] == "memory body"
def test_memories_rerun_does_not_duplicate(
self, pg_conn, mongo_db, seeded_tool
):
# The unique index is (user_id, tool_id, path). Same-row re-insert
# is expected to be a no-op because the SQL uses DO NOTHING.
mongo_db["memories"].insert_one(
{
"_id": "444444444444444444444444",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"path": "/foo",
"content": "v1",
}
)
_backfill_memories(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_memories(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text(
"SELECT count(*) FROM memories "
"WHERE user_id = 'alice' AND path = '/foo'"
)
).scalar()
assert count == 1
def test_memories_rerun_does_not_converge_content(
self, pg_conn, mongo_db, seeded_tool
):
# KNOWN non-idempotent behavior: memories have no legacy_mongo_id
# column and the SQL uses ``ON CONFLICT DO NOTHING`` rather than
# DO UPDATE. A content change in Mongo is NOT reflected in PG on
# re-run. See migration-postgres.md for the tracked fix. When the
# backfill gains a legacy_mongo_id column + DO UPDATE branch,
# flip this assertion to assert the new content wins.
mongo_db["memories"].insert_one(
{
"_id": "444444444444444444444444",
"user_id": "alice",
"tool_id": _TOOL_MONGO_ID,
"path": "/foo",
"content": "original",
}
)
_backfill_memories(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["memories"].update_one(
{"_id": "444444444444444444444444"},
{"$set": {"content": "updated"}},
)
_backfill_memories(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
content = pg_conn.execute(
text(
"SELECT content FROM memories "
"WHERE user_id = 'alice' AND path = '/foo'"
)
).scalar()
# Lock in the non-convergent behavior.
assert content == "original"

View File

@@ -1,348 +0,0 @@
"""Integration tests for ``_backfill_users`` / ``_backfill_prompts`` /
``_backfill_user_tools`` against an ephemeral Postgres.
These exercise the Mongo→PG shape translations end-to-end: a fake Mongo
(mongomock) is populated with representative docs, the ``_backfill_*``
function under test runs against ``pg_conn``, and the resulting rows are
asserted to carry the translated fields. Each function is also run twice
to validate idempotency (no row duplication) and — where the SQL uses
``DO UPDATE`` — convergence on mutated source data.
"""
from __future__ import annotations
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import mongomock
import pytest
from sqlalchemy import text
# Make ``scripts.db.backfill`` importable (scripts/ isn't on sys.path by default).
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
from scripts.db.backfill import ( # noqa: E402
SYSTEM_USER_ID,
_backfill_prompts,
_backfill_user_tools,
_backfill_users,
)
@pytest.fixture
def mongo_db() -> Any:
"""Fresh in-memory Mongo database per test."""
client = mongomock.MongoClient()
return client["docsgpt_test"]
# ---------------------------------------------------------------------------
# users
# ---------------------------------------------------------------------------
class TestBackfillUsers:
def test_users_happy_shape_merges_agent_preferences(self, pg_conn, mongo_db):
mongo_db["users"].insert_many(
[
{
"user_id": "alice",
"agent_preferences": {
"pinned": ["agent-1"],
"theme": "dark",
},
},
{
"user_id": "bob",
"agent_preferences": {},
},
]
)
stats = _backfill_users(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 2
assert stats["written"] == 2
assert stats["skipped_no_user_id"] == 0
rows = pg_conn.execute(
text(
"SELECT user_id, agent_preferences FROM users "
"WHERE user_id IN ('alice', 'bob') ORDER BY user_id"
)
).fetchall()
assert len(rows) == 2
alice = rows[0]._mapping
bob = rows[1]._mapping
# Unknown top-level prefs (``theme``) survive untouched.
assert alice["agent_preferences"]["theme"] == "dark"
assert alice["agent_preferences"]["pinned"] == ["agent-1"]
# Missing ``shared_with_me`` gets filled to [].
assert alice["agent_preferences"]["shared_with_me"] == []
assert bob["agent_preferences"]["pinned"] == []
assert bob["agent_preferences"]["shared_with_me"] == []
def test_users_skips_rows_without_user_id(self, pg_conn, mongo_db):
mongo_db["users"].insert_many(
[{"user_id": "alice"}, {"agent_preferences": {"pinned": []}}]
)
stats = _backfill_users(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 2
assert stats["skipped_no_user_id"] == 1
assert stats["written"] == 1
def test_users_rerun_does_not_duplicate(self, pg_conn, mongo_db):
mongo_db["users"].insert_one(
{"user_id": "alice", "agent_preferences": {"pinned": ["a-1"]}}
)
_backfill_users(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_users(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text("SELECT count(*) FROM users WHERE user_id = 'alice'")
).scalar()
assert count == 1
def test_users_rerun_converges_on_mutated_pinned(self, pg_conn, mongo_db):
mongo_db["users"].insert_one(
{"user_id": "alice", "agent_preferences": {"pinned": ["a-1"]}}
)
_backfill_users(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
# Mutate Mongo.
mongo_db["users"].update_one(
{"user_id": "alice"},
{"$set": {"agent_preferences": {"pinned": ["a-1", "a-2"]}}},
)
_backfill_users(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
prefs = pg_conn.execute(
text("SELECT agent_preferences FROM users WHERE user_id = 'alice'")
).scalar()
assert prefs["pinned"] == ["a-1", "a-2"]
# ---------------------------------------------------------------------------
# prompts
# ---------------------------------------------------------------------------
class TestBackfillPrompts:
def test_prompts_happy_shape(self, pg_conn, mongo_db):
mongo_db["prompts"].insert_many(
[
{
"_id": "507f1f77bcf86cd799439011",
"user": "alice",
"name": "Greet",
"content": "Say hi",
},
{
"_id": "507f1f77bcf86cd799439012",
"user": "system",
"name": "Template",
"content": "Seed",
},
]
)
stats = _backfill_prompts(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
assert stats["seen"] == 2
assert stats["written"] == 2
rows = pg_conn.execute(
text(
"SELECT user_id, name, content, legacy_mongo_id "
"FROM prompts ORDER BY name"
)
).fetchall()
assert len(rows) == 2
greet = rows[0]._mapping
template = rows[1]._mapping
assert greet["user_id"] == "alice"
assert greet["name"] == "Greet"
assert greet["content"] == "Say hi"
assert greet["legacy_mongo_id"] == "507f1f77bcf86cd799439011"
# Legacy ``user="system"`` collapses to the sentinel.
assert template["user_id"] == SYSTEM_USER_ID
def test_prompts_rerun_does_not_duplicate(self, pg_conn, mongo_db):
mongo_db["prompts"].insert_one(
{
"_id": "507f1f77bcf86cd799439011",
"user": "alice",
"name": "Greet",
"content": "Say hi",
}
)
_backfill_prompts(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_prompts(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text(
"SELECT count(*) FROM prompts WHERE "
"legacy_mongo_id = '507f1f77bcf86cd799439011'"
)
).scalar()
assert count == 1
def test_prompts_rerun_converges_on_content(self, pg_conn, mongo_db):
mongo_db["prompts"].insert_one(
{
"_id": "507f1f77bcf86cd799439011",
"user": "alice",
"name": "Greet",
"content": "v1",
}
)
_backfill_prompts(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["prompts"].update_one(
{"_id": "507f1f77bcf86cd799439011"},
{"$set": {"content": "v2"}},
)
_backfill_prompts(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
row = pg_conn.execute(
text(
"SELECT content FROM prompts WHERE "
"legacy_mongo_id = '507f1f77bcf86cd799439011'"
)
).scalar()
assert row == "v2"
# ---------------------------------------------------------------------------
# user_tools
# ---------------------------------------------------------------------------
class TestBackfillUserTools:
def test_user_tools_nested_values_stringified(self, pg_conn, mongo_db):
# Use a dict with a datetime-like / non-JSON-native type nested
# inside config to verify ``default=str`` gets applied.
mongo_db["user_tools"].insert_one(
{
"_id": "507f1f77bcf86cd799439011",
"user": "alice",
"name": "calendar",
"displayName": "Calendar",
"customName": "My Calendar",
"description": "cal desc",
"config": {
"api_key": "secret",
"expires_at": datetime(2026, 1, 1, tzinfo=timezone.utc),
},
"configRequirements": {"api_key": {"type": "string"}},
"actions": [{"name": "create_event"}],
"status": True,
}
)
_backfill_user_tools(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
row = pg_conn.execute(
text(
"SELECT name, custom_name, display_name, description, "
"config, config_requirements, actions, status "
"FROM user_tools WHERE legacy_mongo_id = :lid"
),
{"lid": "507f1f77bcf86cd799439011"},
).one()._mapping
assert row["name"] == "calendar"
assert row["custom_name"] == "My Calendar"
assert row["display_name"] == "Calendar"
assert row["description"] == "cal desc"
assert row["status"] is True
# Nested config datetime is serialized to a string (default=str).
assert row["config"]["api_key"] == "secret"
assert isinstance(row["config"]["expires_at"], str)
assert row["config_requirements"] == {"api_key": {"type": "string"}}
assert row["actions"] == [{"name": "create_event"}]
def test_user_tools_rerun_converges(self, pg_conn, mongo_db):
mongo_db["user_tools"].insert_one(
{
"_id": "507f1f77bcf86cd799439011",
"user": "alice",
"name": "cal",
"config": {"k": "v1"},
}
)
_backfill_user_tools(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
mongo_db["user_tools"].update_one(
{"_id": "507f1f77bcf86cd799439011"},
{"$set": {"config": {"k": "v2"}, "description": "new"}},
)
_backfill_user_tools(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
rows = pg_conn.execute(
text(
"SELECT config, description FROM user_tools "
"WHERE legacy_mongo_id = '507f1f77bcf86cd799439011'"
)
).fetchall()
assert len(rows) == 1
assert rows[0]._mapping["config"] == {"k": "v2"}
assert rows[0]._mapping["description"] == "new"
def test_user_tools_rerun_does_not_duplicate(self, pg_conn, mongo_db):
mongo_db["user_tools"].insert_one(
{
"_id": "507f1f77bcf86cd799439011",
"user": "alice",
"name": "cal",
}
)
_backfill_user_tools(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
_backfill_user_tools(
conn=pg_conn, mongo_db=mongo_db, batch_size=100, dry_run=False
)
count = pg_conn.execute(
text(
"SELECT count(*) FROM user_tools "
"WHERE legacy_mongo_id = '507f1f77bcf86cd799439011'"
)
).scalar()
assert count == 1

View File

@@ -10,7 +10,7 @@ Two invariants are covered here:
These tests run against a real ephemeral Postgres (via ``pg_engine``)
rather than mocks. They rebuild the module-level engine cache so the
``statement_timeout`` ``connect_args`` is actually exercised.
engine factory's ``statement_timeout`` setup is actually exercised.
"""
from __future__ import annotations
@@ -35,9 +35,9 @@ def wired_engine(pg_engine, monkeypatch):
"""Rebuild the module-level engine against the ephemeral DB.
``pg_engine`` already creates its own SQLAlchemy engine, but that
engine does not carry our ``statement_timeout`` ``connect_args``. We
reconstruct one via :func:`get_engine` so the real production factory
code path is exercised.
engine does not install our ``statement_timeout`` connect-event
hook. We reconstruct one via :func:`get_engine` so the real
production factory code path is exercised.
"""
# Reset the module-level cache so get_engine() re-reads the URL and
# applies the production connect_args.
@@ -129,7 +129,8 @@ class TestDbReadonlyEnforcement:
class TestStatementTimeout:
"""The engine factory installs ``statement_timeout`` via connect_args.
"""The engine factory installs ``statement_timeout`` on every new
connection.
We verify two things: