model-dispatcher 0.3.0__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 (151) hide show
  1. model_dispatcher-0.3.0/.dockerignore +13 -0
  2. model_dispatcher-0.3.0/.github/workflows/ci.yml +275 -0
  3. model_dispatcher-0.3.0/.github/workflows/release.yml +221 -0
  4. model_dispatcher-0.3.0/.gitignore +218 -0
  5. model_dispatcher-0.3.0/ARCHITECTURE.md +457 -0
  6. model_dispatcher-0.3.0/Dockerfile +36 -0
  7. model_dispatcher-0.3.0/LICENSE +21 -0
  8. model_dispatcher-0.3.0/PKG-INFO +169 -0
  9. model_dispatcher-0.3.0/README.md +120 -0
  10. model_dispatcher-0.3.0/clients/browser-agent/.gitignore +3 -0
  11. model_dispatcher-0.3.0/clients/browser-agent/README.md +98 -0
  12. model_dispatcher-0.3.0/clients/browser-agent/package-lock.json +1455 -0
  13. model_dispatcher-0.3.0/clients/browser-agent/package.json +43 -0
  14. model_dispatcher-0.3.0/clients/browser-agent/src/agent.ts +88 -0
  15. model_dispatcher-0.3.0/clients/browser-agent/src/config.ts +96 -0
  16. model_dispatcher-0.3.0/clients/browser-agent/src/externalChat.ts +115 -0
  17. model_dispatcher-0.3.0/clients/browser-agent/src/http.ts +100 -0
  18. model_dispatcher-0.3.0/clients/browser-agent/src/index.ts +36 -0
  19. model_dispatcher-0.3.0/clients/browser-agent/src/messages.ts +61 -0
  20. model_dispatcher-0.3.0/clients/browser-agent/src/providers/anthropic.ts +86 -0
  21. model_dispatcher-0.3.0/clients/browser-agent/src/providers/gemini.ts +95 -0
  22. model_dispatcher-0.3.0/clients/browser-agent/src/providers/ollama.ts +81 -0
  23. model_dispatcher-0.3.0/clients/browser-agent/src/providers/openaiCompatible.ts +89 -0
  24. model_dispatcher-0.3.0/clients/browser-agent/src/registry.ts +60 -0
  25. model_dispatcher-0.3.0/clients/browser-agent/src/types.ts +79 -0
  26. model_dispatcher-0.3.0/clients/browser-agent/tests/agent.test.ts +92 -0
  27. model_dispatcher-0.3.0/clients/browser-agent/tests/config.test.ts +75 -0
  28. model_dispatcher-0.3.0/clients/browser-agent/tests/externalChat.test.ts +103 -0
  29. model_dispatcher-0.3.0/clients/browser-agent/tests/http.test.ts +140 -0
  30. model_dispatcher-0.3.0/clients/browser-agent/tests/messages.test.ts +64 -0
  31. model_dispatcher-0.3.0/clients/browser-agent/tests/providers/anthropic.test.ts +58 -0
  32. model_dispatcher-0.3.0/clients/browser-agent/tests/providers/gemini.test.ts +70 -0
  33. model_dispatcher-0.3.0/clients/browser-agent/tests/providers/ollama.test.ts +59 -0
  34. model_dispatcher-0.3.0/clients/browser-agent/tests/providers/openaiCompatible.test.ts +68 -0
  35. model_dispatcher-0.3.0/clients/browser-agent/tsconfig.build.json +9 -0
  36. model_dispatcher-0.3.0/clients/browser-agent/tsconfig.json +18 -0
  37. model_dispatcher-0.3.0/clients/typescript/.gitignore +3 -0
  38. model_dispatcher-0.3.0/clients/typescript/package-lock.json +1505 -0
  39. model_dispatcher-0.3.0/clients/typescript/package.json +55 -0
  40. model_dispatcher-0.3.0/clients/typescript/src/client.ts +148 -0
  41. model_dispatcher-0.3.0/clients/typescript/src/events.ts +77 -0
  42. model_dispatcher-0.3.0/clients/typescript/src/index.ts +56 -0
  43. model_dispatcher-0.3.0/clients/typescript/src/interceptors/appcheck.ts +33 -0
  44. model_dispatcher-0.3.0/clients/typescript/src/interceptors/auth.ts +34 -0
  45. model_dispatcher-0.3.0/clients/typescript/src/interceptors/handoff.ts +91 -0
  46. model_dispatcher-0.3.0/clients/typescript/src/interceptors/interceptor.ts +53 -0
  47. model_dispatcher-0.3.0/clients/typescript/src/interceptors/retry.ts +92 -0
  48. model_dispatcher-0.3.0/clients/typescript/src/interceptors/timeout.ts +48 -0
  49. model_dispatcher-0.3.0/clients/typescript/src/react/useGateway.ts +67 -0
  50. model_dispatcher-0.3.0/clients/typescript/src/types.ts +112 -0
  51. model_dispatcher-0.3.0/clients/typescript/tests/client.test.ts +204 -0
  52. model_dispatcher-0.3.0/clients/typescript/tests/units.test.ts +79 -0
  53. model_dispatcher-0.3.0/clients/typescript/tsconfig.build.json +9 -0
  54. model_dispatcher-0.3.0/clients/typescript/tsconfig.json +19 -0
  55. model_dispatcher-0.3.0/demo/README.md +55 -0
  56. model_dispatcher-0.3.0/demo/backend/app.py +190 -0
  57. model_dispatcher-0.3.0/demo/backend/requirements.txt +5 -0
  58. model_dispatcher-0.3.0/demo/frontend/.gitignore +5 -0
  59. model_dispatcher-0.3.0/demo/frontend/index.html +12 -0
  60. model_dispatcher-0.3.0/demo/frontend/package-lock.json +1732 -0
  61. model_dispatcher-0.3.0/demo/frontend/package.json +22 -0
  62. model_dispatcher-0.3.0/demo/frontend/src/App.tsx +203 -0
  63. model_dispatcher-0.3.0/demo/frontend/src/api.ts +75 -0
  64. model_dispatcher-0.3.0/demo/frontend/src/main.tsx +10 -0
  65. model_dispatcher-0.3.0/demo/frontend/src/styles.css +307 -0
  66. model_dispatcher-0.3.0/demo/frontend/tsconfig.json +20 -0
  67. model_dispatcher-0.3.0/demo/frontend/vite.config.ts +18 -0
  68. model_dispatcher-0.3.0/docs/USAGE.md +183 -0
  69. model_dispatcher-0.3.0/docs/hld/hld.md +424 -0
  70. model_dispatcher-0.3.0/docs/lld/lld.md +888 -0
  71. model_dispatcher-0.3.0/examples/basic_agent.py +120 -0
  72. model_dispatcher-0.3.0/pyproject.toml +85 -0
  73. model_dispatcher-0.3.0/src/model_dispatcher/__init__.py +107 -0
  74. model_dispatcher-0.3.0/src/model_dispatcher/_async_bridge.py +50 -0
  75. model_dispatcher-0.3.0/src/model_dispatcher/config.py +99 -0
  76. model_dispatcher-0.3.0/src/model_dispatcher/exceptions.py +134 -0
  77. model_dispatcher-0.3.0/src/model_dispatcher/fallback/__init__.py +35 -0
  78. model_dispatcher-0.3.0/src/model_dispatcher/fallback/chain.py +112 -0
  79. model_dispatcher-0.3.0/src/model_dispatcher/fallback/conditions.py +44 -0
  80. model_dispatcher-0.3.0/src/model_dispatcher/fallback/handlers.py +549 -0
  81. model_dispatcher-0.3.0/src/model_dispatcher/gateway.py +181 -0
  82. model_dispatcher-0.3.0/src/model_dispatcher/observability/__init__.py +8 -0
  83. model_dispatcher-0.3.0/src/model_dispatcher/observability/logging.py +28 -0
  84. model_dispatcher-0.3.0/src/model_dispatcher/observability/metrics.py +35 -0
  85. model_dispatcher-0.3.0/src/model_dispatcher/onboarding/__init__.py +14 -0
  86. model_dispatcher-0.3.0/src/model_dispatcher/onboarding/flow.py +69 -0
  87. model_dispatcher-0.3.0/src/model_dispatcher/onboarding/handoff.py +96 -0
  88. model_dispatcher-0.3.0/src/model_dispatcher/orchestration/__init__.py +19 -0
  89. model_dispatcher-0.3.0/src/model_dispatcher/orchestration/loop.py +203 -0
  90. model_dispatcher-0.3.0/src/model_dispatcher/orchestration/result.py +54 -0
  91. model_dispatcher-0.3.0/src/model_dispatcher/orchestration/state.py +60 -0
  92. model_dispatcher-0.3.0/src/model_dispatcher/orchestration/tools.py +110 -0
  93. model_dispatcher-0.3.0/src/model_dispatcher/providers/__init__.py +33 -0
  94. model_dispatcher-0.3.0/src/model_dispatcher/providers/anthropic_provider.py +216 -0
  95. model_dispatcher-0.3.0/src/model_dispatcher/providers/base.py +102 -0
  96. model_dispatcher-0.3.0/src/model_dispatcher/providers/gemini_provider.py +245 -0
  97. model_dispatcher-0.3.0/src/model_dispatcher/providers/mock_provider.py +127 -0
  98. model_dispatcher-0.3.0/src/model_dispatcher/providers/openai_compatible.py +117 -0
  99. model_dispatcher-0.3.0/src/model_dispatcher/providers/openai_provider.py +218 -0
  100. model_dispatcher-0.3.0/src/model_dispatcher/providers/registry.py +77 -0
  101. model_dispatcher-0.3.0/src/model_dispatcher/providers/retry_hints.py +79 -0
  102. model_dispatcher-0.3.0/src/model_dispatcher/py.typed +0 -0
  103. model_dispatcher-0.3.0/src/model_dispatcher/quota/__init__.py +19 -0
  104. model_dispatcher-0.3.0/src/model_dispatcher/quota/manager.py +139 -0
  105. model_dispatcher-0.3.0/src/model_dispatcher/quota/store.py +96 -0
  106. model_dispatcher-0.3.0/src/model_dispatcher/quota/tenant.py +63 -0
  107. model_dispatcher-0.3.0/src/model_dispatcher/quota/tokenizer.py +58 -0
  108. model_dispatcher-0.3.0/src/model_dispatcher/routing/__init__.py +8 -0
  109. model_dispatcher-0.3.0/src/model_dispatcher/routing/router.py +65 -0
  110. model_dispatcher-0.3.0/src/model_dispatcher/routing/triage.py +90 -0
  111. model_dispatcher-0.3.0/src/model_dispatcher/security/__init__.py +15 -0
  112. model_dispatcher-0.3.0/src/model_dispatcher/security/credentials.py +179 -0
  113. model_dispatcher-0.3.0/src/model_dispatcher/security/perimeter.py +80 -0
  114. model_dispatcher-0.3.0/src/model_dispatcher/security/redaction.py +62 -0
  115. model_dispatcher-0.3.0/src/model_dispatcher/types.py +229 -0
  116. model_dispatcher-0.3.0/templates/vercel-app/.env.example +37 -0
  117. model_dispatcher-0.3.0/templates/vercel-app/.gitignore +9 -0
  118. model_dispatcher-0.3.0/templates/vercel-app/FIREBASE_APPCHECK_SETUP.md +159 -0
  119. model_dispatcher-0.3.0/templates/vercel-app/README.md +141 -0
  120. model_dispatcher-0.3.0/templates/vercel-app/api/_lib/__init__.py +6 -0
  121. model_dispatcher-0.3.0/templates/vercel-app/api/_lib/appcheck.py +128 -0
  122. model_dispatcher-0.3.0/templates/vercel-app/api/_lib/auth.py +148 -0
  123. model_dispatcher-0.3.0/templates/vercel-app/api/_lib/firebase_app.py +66 -0
  124. model_dispatcher-0.3.0/templates/vercel-app/api/_lib/http.py +85 -0
  125. model_dispatcher-0.3.0/templates/vercel-app/api/_lib/pipeline.py +102 -0
  126. model_dispatcher-0.3.0/templates/vercel-app/api/_lib/wiring.py +164 -0
  127. model_dispatcher-0.3.0/templates/vercel-app/api/gateway.py +56 -0
  128. model_dispatcher-0.3.0/templates/vercel-app/api/requirements.txt +13 -0
  129. model_dispatcher-0.3.0/templates/vercel-app/api/tests/conftest.py +14 -0
  130. model_dispatcher-0.3.0/templates/vercel-app/api/tests/test_auth.py +85 -0
  131. model_dispatcher-0.3.0/templates/vercel-app/api/tests/test_firebase_app.py +42 -0
  132. model_dispatcher-0.3.0/templates/vercel-app/api/tests/test_pipeline.py +193 -0
  133. model_dispatcher-0.3.0/templates/vercel-app/api/tests/test_wiring.py +52 -0
  134. model_dispatcher-0.3.0/templates/vercel-app/app/components/KeyWizard.tsx +58 -0
  135. model_dispatcher-0.3.0/templates/vercel-app/app/globals.css +107 -0
  136. model_dispatcher-0.3.0/templates/vercel-app/app/layout.tsx +22 -0
  137. model_dispatcher-0.3.0/templates/vercel-app/app/lib/gateway.ts +124 -0
  138. model_dispatcher-0.3.0/templates/vercel-app/app/page.tsx +82 -0
  139. model_dispatcher-0.3.0/templates/vercel-app/package.json +25 -0
  140. model_dispatcher-0.3.0/templates/vercel-app/tsconfig.json +20 -0
  141. model_dispatcher-0.3.0/templates/vercel-app/vercel.json +8 -0
  142. model_dispatcher-0.3.0/tests/conftest.py +78 -0
  143. model_dispatcher-0.3.0/tests/test_agent_loop.py +106 -0
  144. model_dispatcher-0.3.0/tests/test_fallback_chain.py +323 -0
  145. model_dispatcher-0.3.0/tests/test_gateway_facade.py +86 -0
  146. model_dispatcher-0.3.0/tests/test_onboarding_handoff.py +96 -0
  147. model_dispatcher-0.3.0/tests/test_providers_adapters.py +291 -0
  148. model_dispatcher-0.3.0/tests/test_quota_manager.py +69 -0
  149. model_dispatcher-0.3.0/tests/test_retry_hints.py +58 -0
  150. model_dispatcher-0.3.0/tests/test_router_triage.py +70 -0
  151. model_dispatcher-0.3.0/tests/test_security_redaction.py +102 -0
@@ -0,0 +1,13 @@
1
+ .git
2
+ .venv
3
+ **/__pycache__
4
+ **/*.pyc
5
+ .mypy_cache
6
+ .ruff_cache
7
+ .pytest_cache
8
+ demo/frontend/node_modules
9
+ demo/frontend/dist
10
+ demo/backend/static
11
+ tests
12
+ *.md
13
+ !README.md
@@ -0,0 +1,275 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["**"]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ python:
14
+ name: Lint, type-check & test (py${{ matrix.python-version }})
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version: ["3.11", "3.12", "3.13"]
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+
28
+ - name: Install package with dev extras
29
+ run: pip install -e ".[dev]"
30
+
31
+ - name: Ruff lint
32
+ run: ruff check src tests examples
33
+
34
+ - name: Ruff format check
35
+ run: ruff format --check src tests examples
36
+
37
+ - name: mypy (strict)
38
+ run: mypy --strict src examples
39
+
40
+ - name: pytest
41
+ run: pytest
42
+
43
+ - name: Run the basic_agent example (smoke test)
44
+ run: python examples/basic_agent.py
45
+
46
+ frontend:
47
+ name: Build React SPA
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+
52
+ - name: Set up Node
53
+ uses: actions/setup-node@v4
54
+ with:
55
+ node-version: "22"
56
+ cache: npm
57
+ cache-dependency-path: demo/frontend/package-lock.json
58
+
59
+ - name: Install dependencies
60
+ working-directory: demo/frontend
61
+ run: npm ci
62
+
63
+ - name: Build
64
+ working-directory: demo/frontend
65
+ run: npm run build
66
+
67
+ ts_client:
68
+ name: TS client — type-check, test & build
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - uses: actions/checkout@v4
72
+
73
+ - name: Set up Node
74
+ uses: actions/setup-node@v4
75
+ with:
76
+ node-version: "22"
77
+ cache: npm
78
+ cache-dependency-path: clients/typescript/package-lock.json
79
+
80
+ - name: Install dependencies
81
+ working-directory: clients/typescript
82
+ run: npm ci
83
+
84
+ - name: Type-check
85
+ working-directory: clients/typescript
86
+ run: npm run typecheck
87
+
88
+ - name: Test
89
+ working-directory: clients/typescript
90
+ run: npm test
91
+
92
+ - name: Build
93
+ working-directory: clients/typescript
94
+ run: npm run build
95
+
96
+ browser_agent:
97
+ name: Browser agent — type-check, test & build
98
+ runs-on: ubuntu-latest
99
+ steps:
100
+ - uses: actions/checkout@v4
101
+
102
+ - name: Set up Node
103
+ uses: actions/setup-node@v4
104
+ with:
105
+ node-version: "22"
106
+ cache: npm
107
+ cache-dependency-path: clients/browser-agent/package-lock.json
108
+
109
+ - name: Install dependencies
110
+ working-directory: clients/browser-agent
111
+ run: npm ci
112
+
113
+ - name: Type-check
114
+ working-directory: clients/browser-agent
115
+ run: npm run typecheck
116
+
117
+ - name: Test
118
+ working-directory: clients/browser-agent
119
+ run: npm test
120
+
121
+ - name: Build
122
+ working-directory: clients/browser-agent
123
+ run: npm run build
124
+
125
+ vercel_template_frontend:
126
+ name: Vercel template — build Next.js frontend
127
+ runs-on: ubuntu-latest
128
+ steps:
129
+ - uses: actions/checkout@v4
130
+
131
+ - name: Set up Node
132
+ uses: actions/setup-node@v4
133
+ with:
134
+ node-version: "22"
135
+ cache: npm
136
+ cache-dependency-path: clients/typescript/package-lock.json
137
+
138
+ - name: Build the TS client
139
+ working-directory: clients/typescript
140
+ run: |
141
+ npm ci
142
+ npm run build
143
+
144
+ - name: Point the template at the workspace TS client
145
+ # @joka-7/modeldispatcher-client is only published on a tagged release
146
+ # (see release.yml) and may not exist yet or may lag this commit.
147
+ # Building against the workspace copy means this check is self-contained
148
+ # and always exercises the client code actually changed in this PR —
149
+ # not a possibly-stale published version.
150
+ working-directory: templates/vercel-app
151
+ run: |
152
+ node -e "
153
+ const fs = require('fs');
154
+ const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
155
+ p.dependencies['@joka-7/modeldispatcher-client'] = 'file:../../clients/typescript';
156
+ fs.writeFileSync('package.json', JSON.stringify(p, null, 2) + '\n');
157
+ "
158
+
159
+ - name: Install template dependencies
160
+ # npm install, not ci: the file: dependency swap above intentionally
161
+ # diverges from package-lock.json.
162
+ working-directory: templates/vercel-app
163
+ run: npm install
164
+
165
+ - name: Type-check
166
+ working-directory: templates/vercel-app
167
+ run: npm run typecheck
168
+
169
+ - name: Build (next build)
170
+ # A real build with zero Firebase env vars set — this is what catches
171
+ # both a missing/broken app/lib/gateway.ts and any Firebase call that
172
+ # isn't properly deferred to runtime (see that file's module doc).
173
+ working-directory: templates/vercel-app
174
+ run: npm run build
175
+ env:
176
+ NEXT_TELEMETRY_DISABLED: "1"
177
+
178
+ vercel_template:
179
+ name: Vercel template — lint & test wrapper (py${{ matrix.python-version }})
180
+ runs-on: ubuntu-latest
181
+ strategy:
182
+ fail-fast: false
183
+ matrix:
184
+ python-version: ["3.11", "3.12", "3.13"]
185
+ steps:
186
+ - uses: actions/checkout@v4
187
+
188
+ - name: Set up Python
189
+ uses: actions/setup-python@v5
190
+ with:
191
+ python-version: ${{ matrix.python-version }}
192
+
193
+ - name: Install gateway (dev extras) + wrapper dependencies
194
+ run: |
195
+ pip install -e ".[dev]"
196
+ pip install firebase-admin
197
+
198
+ - name: Ruff lint
199
+ run: ruff check templates
200
+
201
+ - name: Ruff format check
202
+ run: ruff format --check templates
203
+
204
+ - name: mypy (lenient — firebase-admin ships no type stubs)
205
+ env:
206
+ MYPYPATH: templates/vercel-app/api
207
+ run: >
208
+ mypy --config-file= --ignore-missing-imports
209
+ templates/vercel-app/api/gateway.py
210
+ templates/vercel-app/api/_lib
211
+
212
+ - name: pytest (dispatch pipeline)
213
+ run: pytest templates/vercel-app/api/tests
214
+
215
+ docker:
216
+ name: Build & publish demo image
217
+ runs-on: ubuntu-latest
218
+ needs: [python, frontend]
219
+ permissions:
220
+ contents: read
221
+ packages: write
222
+ steps:
223
+ - uses: actions/checkout@v4
224
+
225
+ - name: Set up Buildx
226
+ uses: docker/setup-buildx-action@v3
227
+
228
+ - name: Log in to GHCR
229
+ # Publish only from the default branch; feature branches and PRs build
230
+ # the image (validating the Dockerfile) without pushing.
231
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
232
+ uses: docker/login-action@v3
233
+ with:
234
+ registry: ghcr.io
235
+ username: ${{ github.actor }}
236
+ password: ${{ secrets.GITHUB_TOKEN }}
237
+
238
+ - name: Image metadata
239
+ id: meta
240
+ uses: docker/metadata-action@v5
241
+ with:
242
+ images: ghcr.io/${{ github.repository }}
243
+ tags: |
244
+ type=ref,event=branch
245
+ type=sha
246
+ type=raw,value=latest,enable={{is_default_branch}}
247
+
248
+ - name: Build (and push from the default branch)
249
+ uses: docker/build-push-action@v6
250
+ with:
251
+ context: .
252
+ push: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
253
+ tags: ${{ steps.meta.outputs.tags }}
254
+ labels: ${{ steps.meta.outputs.labels }}
255
+ cache-from: type=gha
256
+ cache-to: type=gha,mode=max
257
+
258
+ # ----------------------------------------------------------------------- #
259
+ # Deploy the published image to a hosting target. This is intentionally a
260
+ # manual, gated stub: wire it to your platform (Fly.io / Render / Cloud Run /
261
+ # a k8s cluster) by adding the deploy step and the platform token as a repo
262
+ # secret. It runs only on the default branch so feature branches don't deploy.
263
+ # ----------------------------------------------------------------------- #
264
+ deploy:
265
+ name: Deploy demo (configure target)
266
+ runs-on: ubuntu-latest
267
+ needs: docker
268
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
269
+ environment: demo
270
+ steps:
271
+ - name: Pending deploy target
272
+ run: |
273
+ echo "Image published to ghcr.io/${{ github.repository }}:latest"
274
+ echo "Add your host's deploy step here (e.g. flyctl deploy / render deploy"
275
+ echo "/ gcloud run deploy) plus its token as a repository secret."
@@ -0,0 +1,221 @@
1
+ name: Release
2
+
3
+ # Triggered by pushing a version tag (e.g. `v0.2.0`). This publishes
4
+ # model-dispatcher to PyPI and the matching @joka-7/modeldispatcher-client
5
+ # build to GitHub Packages, and is what makes the git-pinned installs in
6
+ # templates/vercel-app/api/requirements.txt (and the README's
7
+ # `git+...@v0.2.0` examples) resolve to a real, published point in history.
8
+ on:
9
+ push:
10
+ tags:
11
+ - "v*.*.*"
12
+
13
+ concurrency:
14
+ group: release-${{ github.ref }}
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ verify:
19
+ name: Re-verify the tagged commit
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+
29
+ - name: Install package with dev extras
30
+ run: |
31
+ pip install -e ".[dev]"
32
+ pip install firebase-admin
33
+
34
+ - name: Ruff lint
35
+ run: ruff check src tests templates
36
+
37
+ - name: Ruff format check
38
+ run: ruff format --check src tests templates
39
+
40
+ - name: mypy (strict, core library)
41
+ run: mypy --strict src
42
+
43
+ - name: pytest (core library + Vercel template)
44
+ run: pytest tests templates/vercel-app/api/tests
45
+
46
+ - name: Set up Node
47
+ uses: actions/setup-node@v4
48
+ with:
49
+ node-version: "22"
50
+ cache: npm
51
+ cache-dependency-path: clients/typescript/package-lock.json
52
+
53
+ - name: Install TS client dependencies
54
+ working-directory: clients/typescript
55
+ run: npm ci
56
+
57
+ - name: TS type-check
58
+ working-directory: clients/typescript
59
+ run: npm run typecheck
60
+
61
+ - name: TS test
62
+ working-directory: clients/typescript
63
+ run: npm test
64
+
65
+ - name: Set up Node (browser agent)
66
+ uses: actions/setup-node@v4
67
+ with:
68
+ node-version: "22"
69
+ cache: npm
70
+ cache-dependency-path: clients/browser-agent/package-lock.json
71
+
72
+ - name: Install browser-agent dependencies
73
+ working-directory: clients/browser-agent
74
+ run: npm ci
75
+
76
+ - name: Browser agent type-check
77
+ working-directory: clients/browser-agent
78
+ run: npm run typecheck
79
+
80
+ - name: Browser agent test
81
+ working-directory: clients/browser-agent
82
+ run: npm test
83
+
84
+ publish_pypi:
85
+ name: Publish model-dispatcher to PyPI
86
+ runs-on: ubuntu-latest
87
+ needs: verify
88
+ environment: pypi
89
+ permissions:
90
+ # Required for PyPI Trusted Publishing (OIDC) — no API token/secret needed.
91
+ # A "pending publisher" must be registered on PyPI first: Account Settings
92
+ # → Publishing, with owner=joka-7, repo=ModelDispatcher, workflow=release.yml.
93
+ # If that publisher was registered *without* an environment name, delete the
94
+ # `environment: pypi` line above to match.
95
+ id-token: write
96
+ contents: read
97
+ steps:
98
+ - uses: actions/checkout@v4
99
+
100
+ - name: Set up Python
101
+ uses: actions/setup-python@v5
102
+ with:
103
+ python-version: "3.12"
104
+
105
+ - name: Install build tooling
106
+ run: pip install build
107
+
108
+ - name: Verify pyproject.toml version matches the pushed tag
109
+ run: |
110
+ tag_version="${GITHUB_REF_NAME#v}"
111
+ pkg_version="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
112
+ if [ "$tag_version" != "$pkg_version" ]; then
113
+ echo "::error::tag v$tag_version does not match pyproject.toml version $pkg_version"
114
+ exit 1
115
+ fi
116
+
117
+ - name: Build sdist + wheel
118
+ run: python -m build
119
+
120
+ - name: Publish to PyPI
121
+ uses: pypa/gh-action-pypi-publish@release/v1
122
+
123
+ publish_ts_client:
124
+ name: Publish @joka-7/modeldispatcher-client
125
+ runs-on: ubuntu-latest
126
+ needs: verify
127
+ permissions:
128
+ contents: read
129
+ packages: write
130
+ steps:
131
+ - uses: actions/checkout@v4
132
+
133
+ - name: Set up Node
134
+ uses: actions/setup-node@v4
135
+ with:
136
+ node-version: "22"
137
+ registry-url: "https://npm.pkg.github.com"
138
+ scope: "@joka-7"
139
+ cache: npm
140
+ cache-dependency-path: clients/typescript/package-lock.json
141
+
142
+ - name: Install dependencies
143
+ working-directory: clients/typescript
144
+ run: npm ci
145
+
146
+ - name: Build
147
+ working-directory: clients/typescript
148
+ run: npm run build
149
+
150
+ - name: Verify package.json version matches the pushed tag
151
+ working-directory: clients/typescript
152
+ run: |
153
+ tag_version="${GITHUB_REF_NAME#v}"
154
+ pkg_version="$(node -p "require('./package.json').version")"
155
+ if [ "$tag_version" != "$pkg_version" ]; then
156
+ echo "::error::tag v$tag_version does not match package.json version $pkg_version"
157
+ exit 1
158
+ fi
159
+
160
+ - name: Publish to GitHub Packages
161
+ working-directory: clients/typescript
162
+ run: npm publish
163
+ env:
164
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
165
+
166
+ publish_browser_agent:
167
+ name: Publish @joka-7/modeldispatcher-browser-agent
168
+ runs-on: ubuntu-latest
169
+ needs: verify
170
+ permissions:
171
+ contents: read
172
+ packages: write
173
+ steps:
174
+ - uses: actions/checkout@v4
175
+
176
+ - name: Set up Node
177
+ uses: actions/setup-node@v4
178
+ with:
179
+ node-version: "22"
180
+ registry-url: "https://npm.pkg.github.com"
181
+ scope: "@joka-7"
182
+ cache: npm
183
+ cache-dependency-path: clients/browser-agent/package-lock.json
184
+
185
+ - name: Install dependencies
186
+ working-directory: clients/browser-agent
187
+ run: npm ci
188
+
189
+ - name: Build
190
+ working-directory: clients/browser-agent
191
+ run: npm run build
192
+
193
+ - name: Verify package.json version matches the pushed tag
194
+ working-directory: clients/browser-agent
195
+ run: |
196
+ tag_version="${GITHUB_REF_NAME#v}"
197
+ pkg_version="$(node -p "require('./package.json').version")"
198
+ if [ "$tag_version" != "$pkg_version" ]; then
199
+ echo "::error::tag v$tag_version does not match package.json version $pkg_version"
200
+ exit 1
201
+ fi
202
+
203
+ - name: Publish to GitHub Packages
204
+ working-directory: clients/browser-agent
205
+ run: npm publish
206
+ env:
207
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
208
+
209
+ create_release:
210
+ name: Create GitHub Release
211
+ runs-on: ubuntu-latest
212
+ needs: [publish_pypi, publish_ts_client, publish_browser_agent]
213
+ permissions:
214
+ contents: write
215
+ steps:
216
+ - uses: actions/checkout@v4
217
+
218
+ - name: Create release with auto-generated notes
219
+ env:
220
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
221
+ run: gh release create "${{ github.ref_name }}" --generate-notes