canvas-chat 0.1.48__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. canvas_chat-0.1.48/.eslintrc.json +29 -0
  2. canvas_chat-0.1.48/.github/dependabot.yml +17 -0
  3. canvas_chat-0.1.48/.github/workflows/auto-release.yaml +206 -0
  4. canvas_chat-0.1.48/.github/workflows/code-style.yaml +30 -0
  5. canvas_chat-0.1.48/.github/workflows/docs.yaml +80 -0
  6. canvas_chat-0.1.48/.github/workflows/modal-deploy.yaml +163 -0
  7. canvas_chat-0.1.48/.github/workflows/tests.yaml +69 -0
  8. canvas_chat-0.1.48/.gitignore +183 -0
  9. canvas_chat-0.1.48/.llamabot/.gitignore +2 -0
  10. canvas_chat-0.1.48/.llamabot/message_log.db +0 -0
  11. canvas_chat-0.1.48/.markdownlint.jsonc +4 -0
  12. canvas_chat-0.1.48/.pre-commit-config.yaml +37 -0
  13. canvas_chat-0.1.48/.prettierrc +10 -0
  14. canvas_chat-0.1.48/.vscode/settings.json +22 -0
  15. canvas_chat-0.1.48/AGENTS.md +817 -0
  16. canvas_chat-0.1.48/PKG-INFO +217 -0
  17. canvas_chat-0.1.48/README.md +189 -0
  18. canvas_chat-0.1.48/config.example.yaml +175 -0
  19. canvas_chat-0.1.48/config.test.yaml +10 -0
  20. canvas_chat-0.1.48/docs/CONTRIBUTING.md +100 -0
  21. canvas_chat-0.1.48/docs/explanation/admin-mode-security.md +195 -0
  22. canvas_chat-0.1.48/docs/explanation/auto-layout.md +140 -0
  23. canvas_chat-0.1.48/docs/explanation/committee-architecture.md +322 -0
  24. canvas_chat-0.1.48/docs/explanation/matrix-evaluation.md +135 -0
  25. canvas_chat-0.1.48/docs/explanation/matrix-resize-behavior.md +152 -0
  26. canvas_chat-0.1.48/docs/explanation/node-protocols.md +211 -0
  27. canvas_chat-0.1.48/docs/explanation/streaming-architecture.md +72 -0
  28. canvas_chat-0.1.48/docs/explanation/url-fetching.md +73 -0
  29. canvas_chat-0.1.48/docs/explanation/webrtc-signaling.md +142 -0
  30. canvas_chat-0.1.48/docs/how-to/admin-mode.md +204 -0
  31. canvas_chat-0.1.48/docs/how-to/create-custom-node-plugins.md +387 -0
  32. canvas_chat-0.1.48/docs/how-to/deep-research.md +252 -0
  33. canvas_chat-0.1.48/docs/how-to/highlight-and-branch.md +268 -0
  34. canvas_chat-0.1.48/docs/how-to/import-pdfs.md +67 -0
  35. canvas_chat-0.1.48/docs/how-to/llm-committee.md +307 -0
  36. canvas_chat-0.1.48/docs/how-to/navigate-nodes.md +46 -0
  37. canvas_chat-0.1.48/docs/how-to/use-images.md +141 -0
  38. canvas_chat-0.1.48/docs/how-to/use-matrix-evaluation.md +94 -0
  39. canvas_chat-0.1.48/docs/how-to/web-search.md +123 -0
  40. canvas_chat-0.1.48/docs/index.md +118 -0
  41. canvas_chat-0.1.48/docs/reference/keyboard-shortcuts.md +178 -0
  42. canvas_chat-0.1.48/docs/reference/search-api.md +152 -0
  43. canvas_chat-0.1.48/docs/releases/.gitkeep +0 -0
  44. canvas_chat-0.1.48/docs/releases/v0.1.0.md +92 -0
  45. canvas_chat-0.1.48/docs/releases/v0.1.1.md +21 -0
  46. canvas_chat-0.1.48/docs/releases/v0.1.10.md +20 -0
  47. canvas_chat-0.1.48/docs/releases/v0.1.11.md +20 -0
  48. canvas_chat-0.1.48/docs/releases/v0.1.12.md +24 -0
  49. canvas_chat-0.1.48/docs/releases/v0.1.13.md +21 -0
  50. canvas_chat-0.1.48/docs/releases/v0.1.14.md +19 -0
  51. canvas_chat-0.1.48/docs/releases/v0.1.15.md +16 -0
  52. canvas_chat-0.1.48/docs/releases/v0.1.16.md +23 -0
  53. canvas_chat-0.1.48/docs/releases/v0.1.17.md +16 -0
  54. canvas_chat-0.1.48/docs/releases/v0.1.18.md +16 -0
  55. canvas_chat-0.1.48/docs/releases/v0.1.19.md +18 -0
  56. canvas_chat-0.1.48/docs/releases/v0.1.2.md +17 -0
  57. canvas_chat-0.1.48/docs/releases/v0.1.20.md +24 -0
  58. canvas_chat-0.1.48/docs/releases/v0.1.21.md +18 -0
  59. canvas_chat-0.1.48/docs/releases/v0.1.22.md +15 -0
  60. canvas_chat-0.1.48/docs/releases/v0.1.23.md +16 -0
  61. canvas_chat-0.1.48/docs/releases/v0.1.24.md +15 -0
  62. canvas_chat-0.1.48/docs/releases/v0.1.25.md +25 -0
  63. canvas_chat-0.1.48/docs/releases/v0.1.26.md +15 -0
  64. canvas_chat-0.1.48/docs/releases/v0.1.27.md +16 -0
  65. canvas_chat-0.1.48/docs/releases/v0.1.28.md +19 -0
  66. canvas_chat-0.1.48/docs/releases/v0.1.29.md +20 -0
  67. canvas_chat-0.1.48/docs/releases/v0.1.3.md +15 -0
  68. canvas_chat-0.1.48/docs/releases/v0.1.30.md +15 -0
  69. canvas_chat-0.1.48/docs/releases/v0.1.31.md +20 -0
  70. canvas_chat-0.1.48/docs/releases/v0.1.32.md +15 -0
  71. canvas_chat-0.1.48/docs/releases/v0.1.33.md +16 -0
  72. canvas_chat-0.1.48/docs/releases/v0.1.34.md +16 -0
  73. canvas_chat-0.1.48/docs/releases/v0.1.35.md +20 -0
  74. canvas_chat-0.1.48/docs/releases/v0.1.36.md +15 -0
  75. canvas_chat-0.1.48/docs/releases/v0.1.37.md +28 -0
  76. canvas_chat-0.1.48/docs/releases/v0.1.38.md +15 -0
  77. canvas_chat-0.1.48/docs/releases/v0.1.39.md +17 -0
  78. canvas_chat-0.1.48/docs/releases/v0.1.4.md +18 -0
  79. canvas_chat-0.1.48/docs/releases/v0.1.40.md +15 -0
  80. canvas_chat-0.1.48/docs/releases/v0.1.41.md +15 -0
  81. canvas_chat-0.1.48/docs/releases/v0.1.42.md +15 -0
  82. canvas_chat-0.1.48/docs/releases/v0.1.43.md +15 -0
  83. canvas_chat-0.1.48/docs/releases/v0.1.44.md +16 -0
  84. canvas_chat-0.1.48/docs/releases/v0.1.45.md +20 -0
  85. canvas_chat-0.1.48/docs/releases/v0.1.46.md +16 -0
  86. canvas_chat-0.1.48/docs/releases/v0.1.47.md +17 -0
  87. canvas_chat-0.1.48/docs/releases/v0.1.48.md +26 -0
  88. canvas_chat-0.1.48/docs/releases/v0.1.5.md +16 -0
  89. canvas_chat-0.1.48/docs/releases/v0.1.6.md +17 -0
  90. canvas_chat-0.1.48/docs/releases/v0.1.7.md +17 -0
  91. canvas_chat-0.1.48/docs/releases/v0.1.8.md +19 -0
  92. canvas_chat-0.1.48/docs/releases/v0.1.9.md +17 -0
  93. canvas_chat-0.1.48/mkdocs.yml +124 -0
  94. canvas_chat-0.1.48/modal_app.py +53 -0
  95. canvas_chat-0.1.48/package-lock.json +2056 -0
  96. canvas_chat-0.1.48/package.json +20 -0
  97. canvas_chat-0.1.48/pixi.lock +5524 -0
  98. canvas_chat-0.1.48/pyproject.toml +79 -0
  99. canvas_chat-0.1.48/src/canvas_chat/__init__.py +30 -0
  100. canvas_chat-0.1.48/src/canvas_chat/__main__.py +152 -0
  101. canvas_chat-0.1.48/src/canvas_chat/app.py +3618 -0
  102. canvas_chat-0.1.48/src/canvas_chat/config.py +279 -0
  103. canvas_chat-0.1.48/src/canvas_chat/static/css/base.css +151 -0
  104. canvas_chat-0.1.48/src/canvas_chat/static/css/canvas.css +150 -0
  105. canvas_chat-0.1.48/src/canvas_chat/static/css/components.css +1075 -0
  106. canvas_chat-0.1.48/src/canvas_chat/static/css/input.css +119 -0
  107. canvas_chat-0.1.48/src/canvas_chat/static/css/matrix.css +312 -0
  108. canvas_chat-0.1.48/src/canvas_chat/static/css/modals.css +1114 -0
  109. canvas_chat-0.1.48/src/canvas_chat/static/css/nodes.css +1647 -0
  110. canvas_chat-0.1.48/src/canvas_chat/static/css/style.css +30 -0
  111. canvas_chat-0.1.48/src/canvas_chat/static/css/toolbar.css +311 -0
  112. canvas_chat-0.1.48/src/canvas_chat/static/index.html +972 -0
  113. canvas_chat-0.1.48/src/canvas_chat/static/js/app.js +5524 -0
  114. canvas_chat-0.1.48/src/canvas_chat/static/js/canvas.js +4087 -0
  115. canvas_chat-0.1.48/src/canvas_chat/static/js/chat.js +333 -0
  116. canvas_chat-0.1.48/src/canvas_chat/static/js/committee.js +536 -0
  117. canvas_chat-0.1.48/src/canvas_chat/static/js/crdt-graph.js +1802 -0
  118. canvas_chat-0.1.48/src/canvas_chat/static/js/event-emitter.js +93 -0
  119. canvas_chat-0.1.48/src/canvas_chat/static/js/factcheck.js +708 -0
  120. canvas_chat-0.1.48/src/canvas_chat/static/js/file-upload-handler.js +269 -0
  121. canvas_chat-0.1.48/src/canvas_chat/static/js/flashcards.js +771 -0
  122. canvas_chat-0.1.48/src/canvas_chat/static/js/graph-types.js +530 -0
  123. canvas_chat-0.1.48/src/canvas_chat/static/js/highlight-utils.js +347 -0
  124. canvas_chat-0.1.48/src/canvas_chat/static/js/layout.js +200 -0
  125. canvas_chat-0.1.48/src/canvas_chat/static/js/matrix.js +835 -0
  126. canvas_chat-0.1.48/src/canvas_chat/static/js/modal-manager.js +578 -0
  127. canvas_chat-0.1.48/src/canvas_chat/static/js/node-protocols.js +1351 -0
  128. canvas_chat-0.1.48/src/canvas_chat/static/js/node-registry.js +325 -0
  129. canvas_chat-0.1.48/src/canvas_chat/static/js/plugins/example-poll-node.js +245 -0
  130. canvas_chat-0.1.48/src/canvas_chat/static/js/pyodide-runner.js +513 -0
  131. canvas_chat-0.1.48/src/canvas_chat/static/js/research.js +479 -0
  132. canvas_chat-0.1.48/src/canvas_chat/static/js/search.js +314 -0
  133. canvas_chat-0.1.48/src/canvas_chat/static/js/slash-command-menu.js +298 -0
  134. canvas_chat-0.1.48/src/canvas_chat/static/js/sse.js +182 -0
  135. canvas_chat-0.1.48/src/canvas_chat/static/js/storage.js +571 -0
  136. canvas_chat-0.1.48/src/canvas_chat/static/js/undo-manager.js +76 -0
  137. canvas_chat-0.1.48/src/canvas_chat/static/js/utils.js +437 -0
  138. canvas_chat-0.1.48/tests/__init__.py +0 -0
  139. canvas_chat-0.1.48/tests/run_tests.js +122 -0
  140. canvas_chat-0.1.48/tests/test_app_init.js +201 -0
  141. canvas_chat-0.1.48/tests/test_canvas_helpers.js +136 -0
  142. canvas_chat-0.1.48/tests/test_config.py +586 -0
  143. canvas_chat-0.1.48/tests/test_crdt_graph.js +640 -0
  144. canvas_chat-0.1.48/tests/test_flashcards.js +396 -0
  145. canvas_chat-0.1.48/tests/test_graph_types.js +190 -0
  146. canvas_chat-0.1.48/tests/test_helpers/README.md +88 -0
  147. canvas_chat-0.1.48/tests/test_helpers/method-binding-test.js +159 -0
  148. canvas_chat-0.1.48/tests/test_layout.js +199 -0
  149. canvas_chat-0.1.48/tests/test_matrix.js +436 -0
  150. canvas_chat-0.1.48/tests/test_models.py +498 -0
  151. canvas_chat-0.1.48/tests/test_node_protocols.js +665 -0
  152. canvas_chat-0.1.48/tests/test_node_registry.js +368 -0
  153. canvas_chat-0.1.48/tests/test_plugin_registration.js +309 -0
  154. canvas_chat-0.1.48/tests/test_search.js +263 -0
  155. canvas_chat-0.1.48/tests/test_setup.js +319 -0
  156. canvas_chat-0.1.48/tests/test_storage.js +808 -0
  157. canvas_chat-0.1.48/tests/test_ui.js +1019 -0
  158. canvas_chat-0.1.48/tests/test_utils.js +153 -0
  159. canvas_chat-0.1.48/tests/test_utils.py +73 -0
  160. canvas_chat-0.1.48/tests/test_utils_basic.js +409 -0
  161. canvas_chat-0.1.48/tests/test_utils_messages.js +335 -0
  162. canvas_chat-0.1.48/uv.lock +4003 -0
@@ -0,0 +1,29 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2022": true
5
+ },
6
+ "parserOptions": {
7
+ "ecmaVersion": 2022,
8
+ "sourceType": "module"
9
+ },
10
+ "globals": {
11
+ "marked": "readonly",
12
+ "katex": "readonly",
13
+ "markedKatex": "readonly",
14
+ "Y": "readonly",
15
+ "IndexeddbPersistence": "readonly",
16
+ "WebrtcProvider": "readonly",
17
+ "pyodideRunner": "readonly"
18
+ },
19
+ "rules": {
20
+ "no-undef": "error",
21
+ "no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
22
+ "no-dupe-keys": "error",
23
+ "no-duplicate-case": "error",
24
+ "no-empty": "warn",
25
+ "no-unreachable": "error",
26
+ "eqeqeq": ["warn", "smart"],
27
+ "no-var": "warn"
28
+ }
29
+ }
@@ -0,0 +1,17 @@
1
+ # Dependabot configuration
2
+ # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
3
+
4
+ version: 2
5
+ updates:
6
+ # Keep GitHub Actions up to date
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
11
+ day: "monday"
12
+ commit-message:
13
+ prefix: "ci"
14
+ labels:
15
+ - "dependencies"
16
+ - "github-actions"
17
+ open-pull-requests-limit: 5
@@ -0,0 +1,206 @@
1
+ name: Auto-release
2
+
3
+ on:
4
+ # Automatic patch release after tests pass on main.
5
+ # This triggers whenever the "Tests" workflow completes.
6
+ # The job-level `if` condition ensures we only release when:
7
+ # 1. Tests passed (conclusion == 'success')
8
+ # 2. It was a push event (not a PR) - i.e., code was merged to main
9
+ workflow_run:
10
+ workflows: ["Tests"]
11
+ types:
12
+ - completed
13
+ branches:
14
+ - main
15
+
16
+ # Manual release for minor/major versions.
17
+ # To cut a new release, navigate to the Actions section of the repo
18
+ # and select this workflow (Auto-release) on the right hand side.
19
+ # Then, click "Run workflow" and select major, minor, or patch.
20
+ workflow_dispatch:
21
+ inputs:
22
+ version_name:
23
+ description: "One of major, minor, or patch"
24
+ required: true
25
+ type: choice
26
+ options:
27
+ - major
28
+ - minor
29
+ - patch
30
+
31
+ permissions:
32
+ contents: write
33
+ id-token: write
34
+
35
+ env:
36
+ DEFAULT_VERSION_NAME: patch
37
+
38
+ jobs:
39
+ release:
40
+ name: Create a new release
41
+ runs-on: ubuntu-latest
42
+ # Run if:
43
+ # 1. Manual trigger (workflow_dispatch), OR
44
+ # 2. Automatic trigger where deploy passed AND it was a push (not PR)
45
+ if: |
46
+ github.event_name == 'workflow_dispatch' ||
47
+ (github.event.workflow_run.conclusion == 'success' &&
48
+ github.event.workflow_run.event == 'push')
49
+
50
+ steps:
51
+ - name: Checkout repository
52
+ uses: actions/checkout@v6
53
+ with:
54
+ fetch-depth: 0
55
+ fetch-tags: true
56
+
57
+ - name: Pull latest commits of main branch
58
+ run: |
59
+ git checkout main
60
+ git pull
61
+
62
+ - name: Check if already released
63
+ id: check_release
64
+ run: |
65
+ CURRENT_COMMIT=$(git rev-parse HEAD)
66
+ echo "Current commit: $CURRENT_COMMIT"
67
+ if git tag --points-at $CURRENT_COMMIT | grep -q "^v"; then
68
+ echo "already_released=true" >> $GITHUB_OUTPUT
69
+ echo "This commit already has a release tag. Will skip release."
70
+ git tag --points-at $CURRENT_COMMIT
71
+ else
72
+ echo "already_released=false" >> $GITHUB_OUTPUT
73
+ echo "No release tag found. Proceeding with release."
74
+ fi
75
+
76
+ - name: Setup Python environment
77
+ if: steps.check_release.outputs.already_released == 'false'
78
+ uses: actions/setup-python@v6
79
+ with:
80
+ python-version: "3.12"
81
+
82
+ - name: Install uv
83
+ if: steps.check_release.outputs.already_released == 'false'
84
+ uses: astral-sh/setup-uv@v7
85
+
86
+ - name: Set version name
87
+ if: steps.check_release.outputs.already_released == 'false'
88
+ run: echo "VERSION_NAME=${{ github.event.inputs.version_name || env.DEFAULT_VERSION_NAME }}" >> $GITHUB_ENV
89
+
90
+ - name: Store current version number
91
+ if: steps.check_release.outputs.already_released == 'false'
92
+ run: echo "current_version=$(uv version --short)" >> $GITHUB_ENV
93
+
94
+ - name: Dry run uv version bump
95
+ if: steps.check_release.outputs.already_released == 'false'
96
+ run: uv version --bump ${{ env.VERSION_NAME }} --dry-run --verbose
97
+
98
+ - name: Store new version number
99
+ if: steps.check_release.outputs.already_released == 'false'
100
+ run: echo "version_number=$(uv version --bump ${{ env.VERSION_NAME }} --dry-run --short)" >> $GITHUB_ENV
101
+
102
+ - name: Display new version number
103
+ if: steps.check_release.outputs.already_released == 'false'
104
+ run: |
105
+ echo "version_name: ${{ env.VERSION_NAME }}"
106
+ echo "version_number: v${{ env.version_number }}"
107
+
108
+ - name: Ensure repo status is clean
109
+ if: steps.check_release.outputs.already_released == 'false'
110
+ run: git status
111
+
112
+ - name: Configure Git
113
+ if: steps.check_release.outputs.already_released == 'false'
114
+ run: |
115
+ git config user.name github-actions
116
+ git config user.email github-actions@github.com
117
+
118
+ - name: Setup pixi
119
+ if: steps.check_release.outputs.already_released == 'false'
120
+ uses: prefix-dev/setup-pixi@v0.9.3
121
+ with:
122
+ run-install: false
123
+
124
+ - name: Install llamabot
125
+ if: steps.check_release.outputs.already_released == 'false'
126
+ run: uv tool install llamabot[cli]
127
+
128
+ - name: Run uv version bump
129
+ if: steps.check_release.outputs.already_released == 'false'
130
+ run: uv version --bump ${{ env.VERSION_NAME }} --verbose
131
+
132
+ - name: Create version tag
133
+ if: steps.check_release.outputs.already_released == 'false'
134
+ run: git tag -a "v${{ env.version_number }}" -m "Release v${{ env.version_number }}"
135
+
136
+ - name: Write release notes
137
+ if: steps.check_release.outputs.already_released == 'false'
138
+ env:
139
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
140
+ run: |
141
+ llamabot configure default-model --model-name="gpt-4.1-mini"
142
+ llamabot git write-release-notes
143
+ # Verify the release notes file was created
144
+ if [ ! -f "docs/releases/v${{ env.version_number }}.md" ]; then
145
+ echo "Error: llamabot git write-release-notes did not create docs/releases/v${{ env.version_number }}.md"
146
+ echo "This indicates llamabot failed or the file was not generated."
147
+ exit 1
148
+ fi
149
+ echo "Release notes file created: docs/releases/v${{ env.version_number }}.md"
150
+
151
+ - name: Run pre-commit to fix any formatting issues
152
+ if: steps.check_release.outputs.already_released == 'false'
153
+ run: |
154
+ # First run applies auto-fixes (may fail if fixes are needed)
155
+ uvx pre-commit run --all-files || true
156
+ # Stage all files (new and modified) including any fixes pre-commit applied
157
+ git add .
158
+ # Second run verifies all checks pass
159
+ uvx pre-commit run --all-files
160
+
161
+ - name: Commit version bump, release notes, and lock file
162
+ if: steps.check_release.outputs.already_released == 'false'
163
+ run: |
164
+ # All files are already staged from the pre-commit step
165
+ git commit -m "Bump version: ${{ env.current_version }} → ${{ env.version_number }}
166
+
167
+ Add release notes for v${{ env.version_number }}"
168
+
169
+ - name: Verify tag creation
170
+ if: steps.check_release.outputs.already_released == 'false'
171
+ run: git tag | grep "v${{ env.version_number }}"
172
+
173
+ - name: Build package
174
+ if: steps.check_release.outputs.already_released == 'false'
175
+ run: uv build --sdist --wheel
176
+
177
+ - name: Publish package (trusted publishing)
178
+ if: steps.check_release.outputs.already_released == 'false'
179
+ run: uv publish --trusted-publishing always
180
+
181
+ - name: Push changes with tags
182
+ if: steps.check_release.outputs.already_released == 'false'
183
+ run: |
184
+ git push && git push --tags
185
+
186
+ - name: Verify release notes file exists before creating release
187
+ if: steps.check_release.outputs.already_released == 'false'
188
+ run: |
189
+ if [ ! -f "docs/releases/v${{ env.version_number }}.md" ]; then
190
+ echo "Error: Release notes file docs/releases/v${{ env.version_number }}.md does not exist"
191
+ echo "This should have been created in the 'Write release notes' step."
192
+ exit 1
193
+ fi
194
+ echo "Release notes file verified: docs/releases/v${{ env.version_number }}.md"
195
+
196
+ - name: Create release in GitHub repo
197
+ if: steps.check_release.outputs.already_released == 'false'
198
+ uses: ncipollo/release-action@v1
199
+ with:
200
+ bodyFile: "docs/releases/v${{ env.version_number }}.md"
201
+ token: ${{ secrets.GITHUB_TOKEN }}
202
+ tag: v${{ env.version_number }}
203
+
204
+ - name: Ensure complete
205
+ if: steps.check_release.outputs.already_released == 'false'
206
+ run: echo "Auto-release complete!"
@@ -0,0 +1,30 @@
1
+ # Run code style checks on each pull request.
2
+ name: Code style checks
3
+
4
+ on:
5
+ pull_request:
6
+ push:
7
+ branches:
8
+ - main
9
+
10
+ jobs:
11
+ linting:
12
+ env:
13
+ # Configure a constant location for the uv cache
14
+ UV_CACHE_DIR: /tmp/.uv-cache
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v7
21
+
22
+ - name: Setup Pixi Environment
23
+ uses: prefix-dev/setup-pixi@v0.9.3
24
+ with:
25
+ pixi-version: latest
26
+ cache: true
27
+ cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
28
+
29
+ - name: Run pre-commit
30
+ run: uvx pre-commit run --all-files
@@ -0,0 +1,80 @@
1
+ name: Build and deploy documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - 'docs/**'
9
+ - 'mkdocs.yml'
10
+ - '.github/workflows/docs.yaml'
11
+ pull_request:
12
+ branches:
13
+ - main
14
+ paths:
15
+ - 'docs/**'
16
+ - 'mkdocs.yml'
17
+ - '.github/workflows/docs.yaml'
18
+
19
+ permissions:
20
+ contents: read
21
+ pages: write
22
+ id-token: write
23
+ pull-requests: write
24
+
25
+ # Allow only one concurrent deployment
26
+ concurrency:
27
+ group: 'pages'
28
+ cancel-in-progress: false
29
+
30
+ jobs:
31
+ build-docs:
32
+ runs-on: ubuntu-latest
33
+ name: Build documentation
34
+
35
+ defaults:
36
+ run:
37
+ shell: bash -l {0}
38
+
39
+ steps:
40
+ - name: Checkout repository
41
+ uses: actions/checkout@v6
42
+ with:
43
+ fetch-depth: 0
44
+
45
+ - name: Setup Pixi Environment
46
+ uses: prefix-dev/setup-pixi@v0.9.3
47
+ with:
48
+ pixi-version: latest
49
+ cache: true
50
+ cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
51
+
52
+ - name: Build docs
53
+ run: pixi run -e docs docs-build
54
+
55
+ - name: Verify build output
56
+ run: |
57
+ echo "✓ Site directory contents:"
58
+ ls -la ./site
59
+ echo "✓ Build verification passed"
60
+
61
+ - name: Upload artifact
62
+ if: github.event_name == 'push' && github.ref_name == 'main'
63
+ uses: actions/upload-pages-artifact@v3
64
+ with:
65
+ path: ./site
66
+
67
+ deploy:
68
+ if: github.event_name == 'push' && github.ref_name == 'main'
69
+ runs-on: ubuntu-latest
70
+ needs: build-docs
71
+ name: Deploy to GitHub Pages
72
+
73
+ environment:
74
+ name: github-pages
75
+ url: ${{ steps.deployment.outputs.page_url }}
76
+
77
+ steps:
78
+ - name: Deploy to GitHub Pages
79
+ id: deployment
80
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,163 @@
1
+ name: Deploy to Modal
2
+
3
+ permissions:
4
+ contents: read
5
+ issues: write
6
+ pull-requests: write
7
+
8
+ on:
9
+ push:
10
+ branches:
11
+ - main
12
+ paths:
13
+ - 'src/**'
14
+ - 'modal_app.py'
15
+ - 'pyproject.toml'
16
+ - 'pixi.lock'
17
+ - '.github/workflows/modal-deploy.yaml'
18
+ pull_request:
19
+ types: [opened, synchronize, reopened]
20
+ paths:
21
+ - 'src/**'
22
+ - 'modal_app.py'
23
+ - 'pyproject.toml'
24
+ - 'pixi.lock'
25
+ - '.github/workflows/modal-deploy.yaml'
26
+
27
+ jobs:
28
+ deploy:
29
+ name: Deploy Canvas Chat
30
+ runs-on: ubuntu-latest
31
+ env:
32
+ MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
33
+ MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
34
+ # Deployed app URLs (not Modal dashboard URLs)
35
+ TEST_APP_URL: "https://ericmjl-test--canvas-chat-fastapi-app.modal.run"
36
+ MAIN_APP_URL: "https://ericmjl--canvas-chat-fastapi-app.modal.run"
37
+ outputs:
38
+ test_url: ${{ steps.deploy-test.outputs.url }}
39
+ main_url: ${{ steps.deploy-main.outputs.url }}
40
+ test_app_url: ${{ steps.deploy-test.outputs.app_url }}
41
+ main_app_url: ${{ steps.deploy-main.outputs.app_url }}
42
+
43
+ steps:
44
+ - name: Checkout repository
45
+ uses: actions/checkout@v6
46
+
47
+ - name: Install uv
48
+ uses: astral-sh/setup-uv@v7
49
+
50
+ - name: Deploy to test environment (PR)
51
+ if: github.event_name == 'pull_request'
52
+ id: deploy-test
53
+ run: |
54
+ OUTPUT=$(uv run --with modal modal deploy modal_app.py -e test 2>&1)
55
+ echo "$OUTPUT"
56
+ URL=$(echo "$OUTPUT" | grep -oE 'https://modal\.com/apps/[^ ]+' | head -1)
57
+ echo "url=$URL" >> $GITHUB_OUTPUT
58
+ echo "app_url=$TEST_APP_URL" >> $GITHUB_OUTPUT
59
+
60
+ - name: Deploy to main environment (push to main)
61
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
62
+ id: deploy-main
63
+ run: |
64
+ OUTPUT=$(uv run --with modal modal deploy modal_app.py 2>&1)
65
+ echo "$OUTPUT"
66
+ URL=$(echo "$OUTPUT" | grep -oE 'https://modal\.com/apps/[^ ]+' | head -1)
67
+ echo "url=$URL" >> $GITHUB_OUTPUT
68
+ echo "app_url=$MAIN_APP_URL" >> $GITHUB_OUTPUT
69
+
70
+ - name: Health check (test deploy)
71
+ if: github.event_name == 'pull_request'
72
+ run: |
73
+ URL="${TEST_APP_URL}/health"
74
+ echo "Checking health for $URL"
75
+ for i in 1 2 3 4 5; do
76
+ status=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time 10 "$URL" || echo "000")
77
+ echo "Attempt $i: HTTP $status"
78
+ if [ "$status" = "200" ]; then
79
+ echo "Deployment healthy"
80
+ exit 0
81
+ fi
82
+ sleep 3
83
+ done
84
+ echo "Deployment health check failed for $URL"
85
+ exit 1
86
+
87
+ - name: Health check (main deploy)
88
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
89
+ run: |
90
+ URL="${MAIN_APP_URL}/health"
91
+ echo "Checking health for $URL"
92
+ for i in 1 2 3 4 5; do
93
+ status=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time 10 "$URL" || echo "000")
94
+ echo "Attempt $i: HTTP $status"
95
+ if [ "$status" = "200" ]; then
96
+ echo "Deployment healthy"
97
+ exit 0
98
+ fi
99
+ sleep 3
100
+ done
101
+ echo "Deployment health check failed for $URL"
102
+ exit 1
103
+
104
+ - name: Write deployment summary
105
+ run: |
106
+ echo "## 🚀 Modal Deployment" >> $GITHUB_STEP_SUMMARY
107
+ echo "" >> $GITHUB_STEP_SUMMARY
108
+ if [ "${{ github.event_name }}" == "pull_request" ]; then
109
+ echo "**Environment:** test" >> $GITHUB_STEP_SUMMARY
110
+ echo "**App URL:** ${TEST_APP_URL}" >> $GITHUB_STEP_SUMMARY
111
+ echo "**Dashboard:** ${{ steps.deploy-test.outputs.url }}" >> $GITHUB_STEP_SUMMARY
112
+ else
113
+ echo "**Environment:** main" >> $GITHUB_STEP_SUMMARY
114
+ echo "**App URL:** ${MAIN_APP_URL}" >> $GITHUB_STEP_SUMMARY
115
+ echo "**Dashboard:** ${{ steps.deploy-main.outputs.url }}" >> $GITHUB_STEP_SUMMARY
116
+ fi
117
+
118
+
119
+ post-pr-comment:
120
+ name: Post PR comment
121
+ runs-on: ubuntu-latest
122
+ needs: deploy
123
+ if: github.event_name == 'pull_request'
124
+ steps:
125
+ - name: Post or update PR comment
126
+ uses: actions/github-script@v8
127
+ with:
128
+ script: |
129
+ const marker = '<!-- modal-deploy-comment -->';
130
+ const appUrl = '${{ needs.deploy.outputs.test_app_url }}';
131
+ const dashboardUrl = '${{ needs.deploy.outputs.test_url }}';
132
+ const body = `${marker}
133
+ ## 🚀 Modal Deployment
134
+
135
+ **Environment:** test
136
+ **App URL:** ${appUrl}
137
+ **Dashboard:** ${dashboardUrl}
138
+
139
+ _This comment is automatically updated on each push._`;
140
+
141
+ const { data: comments } = await github.rest.issues.listComments({
142
+ owner: context.repo.owner,
143
+ repo: context.repo.repo,
144
+ issue_number: context.issue.number,
145
+ });
146
+
147
+ const existing = comments.find(c => c.body.includes(marker));
148
+
149
+ if (existing) {
150
+ await github.rest.issues.updateComment({
151
+ owner: context.repo.owner,
152
+ repo: context.repo.repo,
153
+ comment_id: existing.id,
154
+ body: body,
155
+ });
156
+ } else {
157
+ await github.rest.issues.createComment({
158
+ owner: context.repo.owner,
159
+ repo: context.repo.repo,
160
+ issue_number: context.issue.number,
161
+ body: body,
162
+ });
163
+ }
@@ -0,0 +1,69 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "src/**"
9
+ - "tests/**"
10
+ - "pyproject.toml"
11
+ - ".github/workflows/tests.yaml"
12
+ pull_request:
13
+ branches:
14
+ - main
15
+ paths:
16
+ - "src/**"
17
+ - "tests/**"
18
+ - "pyproject.toml"
19
+ - "package.json"
20
+ - "package-lock.json"
21
+ - ".github/workflows/tests.yaml"
22
+
23
+ concurrency:
24
+ group: ${{ github.workflow }}-${{ github.ref }}
25
+ cancel-in-progress: true
26
+
27
+ jobs:
28
+ python-tests:
29
+ name: Python tests (Python ${{ matrix.python-version }})
30
+ runs-on: ubuntu-latest
31
+ strategy:
32
+ fail-fast: false
33
+ matrix:
34
+ python-version: ["3.11", "3.12", "3.13"]
35
+
36
+ steps:
37
+ - name: Checkout repository
38
+ uses: actions/checkout@v6
39
+
40
+ - name: Setup Python
41
+ uses: actions/setup-python@v6
42
+ with:
43
+ python-version: ${{ matrix.python-version }}
44
+
45
+ - name: Install uv
46
+ uses: astral-sh/setup-uv@v7
47
+
48
+ - name: Install dependencies
49
+ run: uv pip install --system -e ".[dev]"
50
+
51
+ - name: Run Python tests
52
+ run: pytest tests/ -v --color=yes
53
+
54
+ javascript-tests:
55
+ name: JavaScript tests
56
+ runs-on: ubuntu-latest
57
+
58
+ steps:
59
+ - name: Checkout repository
60
+ uses: actions/checkout@v6
61
+
62
+ - name: Install pixi
63
+ uses: prefix-dev/setup-pixi@v0.9.3
64
+
65
+ - name: Install npm dependencies
66
+ run: pixi run npm install
67
+
68
+ - name: Run JavaScript tests
69
+ run: pixi run test-js