JAMES HAN Claude Opus 4.6 (1M context) commited on
Commit
5e35f2a
Β·
1 Parent(s): 9137c77

feat: add GCP Vertex AI auth for Claude (alongside direct API key)

Browse files

- New shared claude-client.ts: API key β†’ GCP service account β†’ GCP ADC
- QC engine, NLI engine, rationale validator all use shared client
- @anthropic-ai/vertex-sdk + google-auth-library added
- Fixed ttal.ts type error (clean build, zero TS errors)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

.env.example CHANGED
@@ -19,8 +19,13 @@ NEXT_PUBLIC_SKIP_AUTH=false
19
  # API Key for Gradio / external client authentication
20
  API_KEY=change-me-to-a-secret-key
21
 
22
- # Anthropic API (NLI Engine)
 
23
  ANTHROPIC_API_KEY=your-anthropic-api-key
 
 
 
 
24
 
25
  # Redis (QC Queue)
26
  REDIS_URL=redis://localhost:6379
 
19
  # API Key for Gradio / external client authentication
20
  API_KEY=change-me-to-a-secret-key
21
 
22
+ # Claude API β€” set ONE of these auth methods:
23
+ # Option 1: Direct Anthropic API key
24
  ANTHROPIC_API_KEY=your-anthropic-api-key
25
+ # Option 2: GCP Vertex AI (base64-encoded service account JSON)
26
+ # GCP_CREDENTIALS_JSON_B64=base64-encoded-json
27
+ # GCP_PROJECT_ID=your-project-id
28
+ # GCP_LOCATION=us-east5
29
 
30
  # Redis (QC Queue)
31
  REDIS_URL=redis://localhost:6379
package-lock.json CHANGED
@@ -10,6 +10,7 @@
10
  "license": "MIT",
11
  "dependencies": {
12
  "@anthropic-ai/sdk": "^0.81.0",
 
13
  "@aws-sdk/client-s3": "^3.1021.0",
14
  "@aws-sdk/s3-request-presigner": "^3.1021.0",
15
  "@hookform/resolvers": "^3.10.0",
@@ -56,6 +57,7 @@
56
  "express": "^4.21.2",
57
  "express-session": "^1.18.1",
58
  "framer-motion": "^11.13.1",
 
59
  "input-otp": "^1.4.2",
60
  "ioredis": "^5.10.1",
61
  "lucide-react": "^0.453.0",
@@ -143,6 +145,105 @@
143
  }
144
  }
145
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  "node_modules/@aws-crypto/crc32": {
147
  "version": "5.2.0",
148
  "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
@@ -6584,6 +6685,15 @@
6584
  "node": ">= 0.6"
6585
  }
6586
  },
 
 
 
 
 
 
 
 
 
6587
  "node_modules/any-promise": {
6588
  "version": "1.3.0",
6589
  "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -6941,6 +7051,15 @@
6941
  "node": ">=0.6"
6942
  }
6943
  },
 
 
 
 
 
 
 
 
 
6944
  "node_modules/binary": {
6945
  "version": "0.3.0",
6946
  "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
@@ -7117,6 +7236,12 @@
7117
  "node": "*"
7118
  }
7119
  },
 
 
 
 
 
 
7120
  "node_modules/buffer-indexof-polyfill": {
7121
  "version": "1.0.2",
7122
  "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
@@ -7622,6 +7747,15 @@
7622
  "node": ">=12"
7623
  }
7624
  },
 
 
 
 
 
 
 
 
 
7625
  "node_modules/date-fns": {
7626
  "version": "3.6.0",
7627
  "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
@@ -7937,6 +8071,15 @@
7937
  "safe-buffer": "~5.1.0"
7938
  }
7939
  },
 
 
 
 
 
 
 
 
 
7940
  "node_modules/ee-first": {
7941
  "version": "1.1.1",
7942
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -8241,6 +8384,12 @@
8241
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
8242
  "license": "MIT"
8243
  },
 
 
 
 
 
 
8244
  "node_modules/fast-csv": {
8245
  "version": "4.3.6",
8246
  "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
@@ -8335,6 +8484,29 @@
8335
  "reusify": "^1.0.4"
8336
  }
8337
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8338
  "node_modules/fill-range": {
8339
  "version": "7.1.1",
8340
  "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -8380,6 +8552,18 @@
8380
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
8381
  "license": "MIT"
8382
  },
 
 
 
 
 
 
 
 
 
 
 
 
8383
  "node_modules/forwarded": {
8384
  "version": "0.2.0",
8385
  "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -8490,6 +8674,34 @@
8490
  "url": "https://github.com/sponsors/ljharb"
8491
  }
8492
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8493
  "node_modules/gensync": {
8494
  "version": "1.0.0-beta.2",
8495
  "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -8592,6 +8804,32 @@
8592
  "node": ">=10.13.0"
8593
  }
8594
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8595
  "node_modules/gopd": {
8596
  "version": "1.2.0",
8597
  "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -8610,6 +8848,68 @@
8610
  "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
8611
  "license": "ISC"
8612
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8613
  "node_modules/has-symbols": {
8614
  "version": "1.1.0",
8615
  "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -8654,6 +8954,19 @@
8654
  "url": "https://opencollective.com/express"
8655
  }
8656
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
8657
  "node_modules/iconv-lite": {
8658
  "version": "0.4.24",
8659
  "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -8818,6 +9131,18 @@
8818
  "node": ">=0.12.0"
8819
  }
8820
  },
 
 
 
 
 
 
 
 
 
 
 
 
8821
  "node_modules/isarray": {
8822
  "version": "1.0.0",
8823
  "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -8862,6 +9187,15 @@
8862
  "node": ">=6"
8863
  }
8864
  },
 
 
 
 
 
 
 
 
 
8865
  "node_modules/json-schema-to-ts": {
8866
  "version": "3.1.1",
8867
  "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz",
@@ -8930,6 +9264,27 @@
8930
  "safe-buffer": "~5.1.0"
8931
  }
8932
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8933
  "node_modules/kysely": {
8934
  "version": "0.28.10",
8935
  "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.10.tgz",
@@ -9784,6 +10139,44 @@
9784
  "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
9785
  "license": "MIT"
9786
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9787
  "node_modules/node-gyp-build": {
9788
  "version": "4.8.4",
9789
  "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
@@ -11459,6 +11852,12 @@
11459
  "node": ">=0.6"
11460
  }
11461
  },
 
 
 
 
 
 
11462
  "node_modules/traverse": {
11463
  "version": "0.3.9",
11464
  "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
@@ -12853,6 +13252,31 @@
12853
  "integrity": "sha512-MSZcA13R9ZlxgYpzfakaSYf8dz5tCdZKYbjtN1qnKbCi+UoyfaTuhvjlXHrITi/fgeO3qWfsH7U3BP1AKnwRNg==",
12854
  "license": "BSD-3-Clause"
12855
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12856
  "node_modules/wouter": {
12857
  "version": "3.9.0",
12858
  "resolved": "https://registry.npmjs.org/wouter/-/wouter-3.9.0.tgz",
 
10
  "license": "MIT",
11
  "dependencies": {
12
  "@anthropic-ai/sdk": "^0.81.0",
13
+ "@anthropic-ai/vertex-sdk": "^0.14.4",
14
  "@aws-sdk/client-s3": "^3.1021.0",
15
  "@aws-sdk/s3-request-presigner": "^3.1021.0",
16
  "@hookform/resolvers": "^3.10.0",
 
57
  "express": "^4.21.2",
58
  "express-session": "^1.18.1",
59
  "framer-motion": "^11.13.1",
60
+ "google-auth-library": "^10.6.2",
61
  "input-otp": "^1.4.2",
62
  "ioredis": "^5.10.1",
63
  "lucide-react": "^0.453.0",
 
145
  }
146
  }
147
  },
148
+ "node_modules/@anthropic-ai/vertex-sdk": {
149
+ "version": "0.14.4",
150
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.14.4.tgz",
151
+ "integrity": "sha512-BZUPRWghZxfSFtAxU563wH+jfWBPoedAwsVxG35FhmNsjeV8tyfN+lFriWhCpcZApxA4NdT6Soov+PzfnxxD5g==",
152
+ "license": "MIT",
153
+ "dependencies": {
154
+ "@anthropic-ai/sdk": ">=0.50.3 <1",
155
+ "google-auth-library": "^9.4.2"
156
+ }
157
+ },
158
+ "node_modules/@anthropic-ai/vertex-sdk/node_modules/gaxios": {
159
+ "version": "6.7.1",
160
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
161
+ "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
162
+ "license": "Apache-2.0",
163
+ "dependencies": {
164
+ "extend": "^3.0.2",
165
+ "https-proxy-agent": "^7.0.1",
166
+ "is-stream": "^2.0.0",
167
+ "node-fetch": "^2.6.9",
168
+ "uuid": "^9.0.1"
169
+ },
170
+ "engines": {
171
+ "node": ">=14"
172
+ }
173
+ },
174
+ "node_modules/@anthropic-ai/vertex-sdk/node_modules/gcp-metadata": {
175
+ "version": "6.1.1",
176
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
177
+ "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
178
+ "license": "Apache-2.0",
179
+ "dependencies": {
180
+ "gaxios": "^6.1.1",
181
+ "google-logging-utils": "^0.0.2",
182
+ "json-bigint": "^1.0.0"
183
+ },
184
+ "engines": {
185
+ "node": ">=14"
186
+ }
187
+ },
188
+ "node_modules/@anthropic-ai/vertex-sdk/node_modules/google-auth-library": {
189
+ "version": "9.15.1",
190
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
191
+ "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
192
+ "license": "Apache-2.0",
193
+ "dependencies": {
194
+ "base64-js": "^1.3.0",
195
+ "ecdsa-sig-formatter": "^1.0.11",
196
+ "gaxios": "^6.1.1",
197
+ "gcp-metadata": "^6.1.0",
198
+ "gtoken": "^7.0.0",
199
+ "jws": "^4.0.0"
200
+ },
201
+ "engines": {
202
+ "node": ">=14"
203
+ }
204
+ },
205
+ "node_modules/@anthropic-ai/vertex-sdk/node_modules/google-logging-utils": {
206
+ "version": "0.0.2",
207
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
208
+ "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
209
+ "license": "Apache-2.0",
210
+ "engines": {
211
+ "node": ">=14"
212
+ }
213
+ },
214
+ "node_modules/@anthropic-ai/vertex-sdk/node_modules/node-fetch": {
215
+ "version": "2.7.0",
216
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
217
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
218
+ "license": "MIT",
219
+ "dependencies": {
220
+ "whatwg-url": "^5.0.0"
221
+ },
222
+ "engines": {
223
+ "node": "4.x || >=6.0.0"
224
+ },
225
+ "peerDependencies": {
226
+ "encoding": "^0.1.0"
227
+ },
228
+ "peerDependenciesMeta": {
229
+ "encoding": {
230
+ "optional": true
231
+ }
232
+ }
233
+ },
234
+ "node_modules/@anthropic-ai/vertex-sdk/node_modules/uuid": {
235
+ "version": "9.0.1",
236
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
237
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
238
+ "funding": [
239
+ "https://github.com/sponsors/broofa",
240
+ "https://github.com/sponsors/ctavan"
241
+ ],
242
+ "license": "MIT",
243
+ "bin": {
244
+ "uuid": "dist/bin/uuid"
245
+ }
246
+ },
247
  "node_modules/@aws-crypto/crc32": {
248
  "version": "5.2.0",
249
  "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
 
6685
  "node": ">= 0.6"
6686
  }
6687
  },
6688
+ "node_modules/agent-base": {
6689
+ "version": "7.1.4",
6690
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
6691
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
6692
+ "license": "MIT",
6693
+ "engines": {
6694
+ "node": ">= 14"
6695
+ }
6696
+ },
6697
  "node_modules/any-promise": {
6698
  "version": "1.3.0",
6699
  "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
 
7051
  "node": ">=0.6"
7052
  }
7053
  },
7054
+ "node_modules/bignumber.js": {
7055
+ "version": "9.3.1",
7056
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
7057
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
7058
+ "license": "MIT",
7059
+ "engines": {
7060
+ "node": "*"
7061
+ }
7062
+ },
7063
  "node_modules/binary": {
7064
  "version": "0.3.0",
7065
  "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
 
7236
  "node": "*"
7237
  }
7238
  },
7239
+ "node_modules/buffer-equal-constant-time": {
7240
+ "version": "1.0.1",
7241
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
7242
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
7243
+ "license": "BSD-3-Clause"
7244
+ },
7245
  "node_modules/buffer-indexof-polyfill": {
7246
  "version": "1.0.2",
7247
  "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
 
7747
  "node": ">=12"
7748
  }
7749
  },
7750
+ "node_modules/data-uri-to-buffer": {
7751
+ "version": "4.0.1",
7752
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
7753
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
7754
+ "license": "MIT",
7755
+ "engines": {
7756
+ "node": ">= 12"
7757
+ }
7758
+ },
7759
  "node_modules/date-fns": {
7760
  "version": "3.6.0",
7761
  "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
 
8071
  "safe-buffer": "~5.1.0"
8072
  }
8073
  },
8074
+ "node_modules/ecdsa-sig-formatter": {
8075
+ "version": "1.0.11",
8076
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
8077
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
8078
+ "license": "Apache-2.0",
8079
+ "dependencies": {
8080
+ "safe-buffer": "^5.0.1"
8081
+ }
8082
+ },
8083
  "node_modules/ee-first": {
8084
  "version": "1.1.1",
8085
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
 
8384
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
8385
  "license": "MIT"
8386
  },
8387
+ "node_modules/extend": {
8388
+ "version": "3.0.2",
8389
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
8390
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
8391
+ "license": "MIT"
8392
+ },
8393
  "node_modules/fast-csv": {
8394
  "version": "4.3.6",
8395
  "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
 
8484
  "reusify": "^1.0.4"
8485
  }
8486
  },
8487
+ "node_modules/fetch-blob": {
8488
+ "version": "3.2.0",
8489
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
8490
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
8491
+ "funding": [
8492
+ {
8493
+ "type": "github",
8494
+ "url": "https://github.com/sponsors/jimmywarting"
8495
+ },
8496
+ {
8497
+ "type": "paypal",
8498
+ "url": "https://paypal.me/jimmywarting"
8499
+ }
8500
+ ],
8501
+ "license": "MIT",
8502
+ "dependencies": {
8503
+ "node-domexception": "^1.0.0",
8504
+ "web-streams-polyfill": "^3.0.3"
8505
+ },
8506
+ "engines": {
8507
+ "node": "^12.20 || >= 14.13"
8508
+ }
8509
+ },
8510
  "node_modules/fill-range": {
8511
  "version": "7.1.1",
8512
  "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
 
8552
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
8553
  "license": "MIT"
8554
  },
8555
+ "node_modules/formdata-polyfill": {
8556
+ "version": "4.0.10",
8557
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
8558
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
8559
+ "license": "MIT",
8560
+ "dependencies": {
8561
+ "fetch-blob": "^3.1.2"
8562
+ },
8563
+ "engines": {
8564
+ "node": ">=12.20.0"
8565
+ }
8566
+ },
8567
  "node_modules/forwarded": {
8568
  "version": "0.2.0",
8569
  "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
 
8674
  "url": "https://github.com/sponsors/ljharb"
8675
  }
8676
  },
8677
+ "node_modules/gaxios": {
8678
+ "version": "7.1.4",
8679
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz",
8680
+ "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==",
8681
+ "license": "Apache-2.0",
8682
+ "dependencies": {
8683
+ "extend": "^3.0.2",
8684
+ "https-proxy-agent": "^7.0.1",
8685
+ "node-fetch": "^3.3.2"
8686
+ },
8687
+ "engines": {
8688
+ "node": ">=18"
8689
+ }
8690
+ },
8691
+ "node_modules/gcp-metadata": {
8692
+ "version": "8.1.2",
8693
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
8694
+ "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
8695
+ "license": "Apache-2.0",
8696
+ "dependencies": {
8697
+ "gaxios": "^7.0.0",
8698
+ "google-logging-utils": "^1.0.0",
8699
+ "json-bigint": "^1.0.0"
8700
+ },
8701
+ "engines": {
8702
+ "node": ">=18"
8703
+ }
8704
+ },
8705
  "node_modules/gensync": {
8706
  "version": "1.0.0-beta.2",
8707
  "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
 
8804
  "node": ">=10.13.0"
8805
  }
8806
  },
8807
+ "node_modules/google-auth-library": {
8808
+ "version": "10.6.2",
8809
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz",
8810
+ "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==",
8811
+ "license": "Apache-2.0",
8812
+ "dependencies": {
8813
+ "base64-js": "^1.3.0",
8814
+ "ecdsa-sig-formatter": "^1.0.11",
8815
+ "gaxios": "^7.1.4",
8816
+ "gcp-metadata": "8.1.2",
8817
+ "google-logging-utils": "1.1.3",
8818
+ "jws": "^4.0.0"
8819
+ },
8820
+ "engines": {
8821
+ "node": ">=18"
8822
+ }
8823
+ },
8824
+ "node_modules/google-logging-utils": {
8825
+ "version": "1.1.3",
8826
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
8827
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
8828
+ "license": "Apache-2.0",
8829
+ "engines": {
8830
+ "node": ">=14"
8831
+ }
8832
+ },
8833
  "node_modules/gopd": {
8834
  "version": "1.2.0",
8835
  "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
 
8848
  "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
8849
  "license": "ISC"
8850
  },
8851
+ "node_modules/gtoken": {
8852
+ "version": "7.1.0",
8853
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
8854
+ "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
8855
+ "license": "MIT",
8856
+ "dependencies": {
8857
+ "gaxios": "^6.0.0",
8858
+ "jws": "^4.0.0"
8859
+ },
8860
+ "engines": {
8861
+ "node": ">=14.0.0"
8862
+ }
8863
+ },
8864
+ "node_modules/gtoken/node_modules/gaxios": {
8865
+ "version": "6.7.1",
8866
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
8867
+ "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
8868
+ "license": "Apache-2.0",
8869
+ "dependencies": {
8870
+ "extend": "^3.0.2",
8871
+ "https-proxy-agent": "^7.0.1",
8872
+ "is-stream": "^2.0.0",
8873
+ "node-fetch": "^2.6.9",
8874
+ "uuid": "^9.0.1"
8875
+ },
8876
+ "engines": {
8877
+ "node": ">=14"
8878
+ }
8879
+ },
8880
+ "node_modules/gtoken/node_modules/node-fetch": {
8881
+ "version": "2.7.0",
8882
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
8883
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
8884
+ "license": "MIT",
8885
+ "dependencies": {
8886
+ "whatwg-url": "^5.0.0"
8887
+ },
8888
+ "engines": {
8889
+ "node": "4.x || >=6.0.0"
8890
+ },
8891
+ "peerDependencies": {
8892
+ "encoding": "^0.1.0"
8893
+ },
8894
+ "peerDependenciesMeta": {
8895
+ "encoding": {
8896
+ "optional": true
8897
+ }
8898
+ }
8899
+ },
8900
+ "node_modules/gtoken/node_modules/uuid": {
8901
+ "version": "9.0.1",
8902
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
8903
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
8904
+ "funding": [
8905
+ "https://github.com/sponsors/broofa",
8906
+ "https://github.com/sponsors/ctavan"
8907
+ ],
8908
+ "license": "MIT",
8909
+ "bin": {
8910
+ "uuid": "dist/bin/uuid"
8911
+ }
8912
+ },
8913
  "node_modules/has-symbols": {
8914
  "version": "1.1.0",
8915
  "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
 
8954
  "url": "https://opencollective.com/express"
8955
  }
8956
  },
8957
+ "node_modules/https-proxy-agent": {
8958
+ "version": "7.0.6",
8959
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
8960
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
8961
+ "license": "MIT",
8962
+ "dependencies": {
8963
+ "agent-base": "^7.1.2",
8964
+ "debug": "4"
8965
+ },
8966
+ "engines": {
8967
+ "node": ">= 14"
8968
+ }
8969
+ },
8970
  "node_modules/iconv-lite": {
8971
  "version": "0.4.24",
8972
  "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
 
9131
  "node": ">=0.12.0"
9132
  }
9133
  },
9134
+ "node_modules/is-stream": {
9135
+ "version": "2.0.1",
9136
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
9137
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
9138
+ "license": "MIT",
9139
+ "engines": {
9140
+ "node": ">=8"
9141
+ },
9142
+ "funding": {
9143
+ "url": "https://github.com/sponsors/sindresorhus"
9144
+ }
9145
+ },
9146
  "node_modules/isarray": {
9147
  "version": "1.0.0",
9148
  "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
 
9187
  "node": ">=6"
9188
  }
9189
  },
9190
+ "node_modules/json-bigint": {
9191
+ "version": "1.0.0",
9192
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
9193
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
9194
+ "license": "MIT",
9195
+ "dependencies": {
9196
+ "bignumber.js": "^9.0.0"
9197
+ }
9198
+ },
9199
  "node_modules/json-schema-to-ts": {
9200
  "version": "3.1.1",
9201
  "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz",
 
9264
  "safe-buffer": "~5.1.0"
9265
  }
9266
  },
9267
+ "node_modules/jwa": {
9268
+ "version": "2.0.1",
9269
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
9270
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
9271
+ "license": "MIT",
9272
+ "dependencies": {
9273
+ "buffer-equal-constant-time": "^1.0.1",
9274
+ "ecdsa-sig-formatter": "1.0.11",
9275
+ "safe-buffer": "^5.0.1"
9276
+ }
9277
+ },
9278
+ "node_modules/jws": {
9279
+ "version": "4.0.1",
9280
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
9281
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
9282
+ "license": "MIT",
9283
+ "dependencies": {
9284
+ "jwa": "^2.0.1",
9285
+ "safe-buffer": "^5.0.1"
9286
+ }
9287
+ },
9288
  "node_modules/kysely": {
9289
  "version": "0.28.10",
9290
  "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.10.tgz",
 
10139
  "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
10140
  "license": "MIT"
10141
  },
10142
+ "node_modules/node-domexception": {
10143
+ "version": "1.0.0",
10144
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
10145
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
10146
+ "deprecated": "Use your platform's native DOMException instead",
10147
+ "funding": [
10148
+ {
10149
+ "type": "github",
10150
+ "url": "https://github.com/sponsors/jimmywarting"
10151
+ },
10152
+ {
10153
+ "type": "github",
10154
+ "url": "https://paypal.me/jimmywarting"
10155
+ }
10156
+ ],
10157
+ "license": "MIT",
10158
+ "engines": {
10159
+ "node": ">=10.5.0"
10160
+ }
10161
+ },
10162
+ "node_modules/node-fetch": {
10163
+ "version": "3.3.2",
10164
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
10165
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
10166
+ "license": "MIT",
10167
+ "dependencies": {
10168
+ "data-uri-to-buffer": "^4.0.0",
10169
+ "fetch-blob": "^3.1.4",
10170
+ "formdata-polyfill": "^4.0.10"
10171
+ },
10172
+ "engines": {
10173
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
10174
+ },
10175
+ "funding": {
10176
+ "type": "opencollective",
10177
+ "url": "https://opencollective.com/node-fetch"
10178
+ }
10179
+ },
10180
  "node_modules/node-gyp-build": {
10181
  "version": "4.8.4",
10182
  "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
 
11852
  "node": ">=0.6"
11853
  }
11854
  },
11855
+ "node_modules/tr46": {
11856
+ "version": "0.0.3",
11857
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
11858
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
11859
+ "license": "MIT"
11860
+ },
11861
  "node_modules/traverse": {
11862
  "version": "0.3.9",
11863
  "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
 
13252
  "integrity": "sha512-MSZcA13R9ZlxgYpzfakaSYf8dz5tCdZKYbjtN1qnKbCi+UoyfaTuhvjlXHrITi/fgeO3qWfsH7U3BP1AKnwRNg==",
13253
  "license": "BSD-3-Clause"
13254
  },
13255
+ "node_modules/web-streams-polyfill": {
13256
+ "version": "3.3.3",
13257
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
13258
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
13259
+ "license": "MIT",
13260
+ "engines": {
13261
+ "node": ">= 8"
13262
+ }
13263
+ },
13264
+ "node_modules/webidl-conversions": {
13265
+ "version": "3.0.1",
13266
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
13267
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
13268
+ "license": "BSD-2-Clause"
13269
+ },
13270
+ "node_modules/whatwg-url": {
13271
+ "version": "5.0.0",
13272
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
13273
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
13274
+ "license": "MIT",
13275
+ "dependencies": {
13276
+ "tr46": "~0.0.3",
13277
+ "webidl-conversions": "^3.0.0"
13278
+ }
13279
+ },
13280
  "node_modules/wouter": {
13281
  "version": "3.9.0",
13282
  "resolved": "https://registry.npmjs.org/wouter/-/wouter-3.9.0.tgz",
package.json CHANGED
@@ -15,6 +15,7 @@
15
  },
16
  "dependencies": {
17
  "@anthropic-ai/sdk": "^0.81.0",
 
18
  "@aws-sdk/client-s3": "^3.1021.0",
19
  "@aws-sdk/s3-request-presigner": "^3.1021.0",
20
  "@hookform/resolvers": "^3.10.0",
@@ -61,6 +62,7 @@
61
  "express": "^4.21.2",
62
  "express-session": "^1.18.1",
63
  "framer-motion": "^11.13.1",
 
64
  "input-otp": "^1.4.2",
65
  "ioredis": "^5.10.1",
66
  "lucide-react": "^0.453.0",
 
15
  },
16
  "dependencies": {
17
  "@anthropic-ai/sdk": "^0.81.0",
18
+ "@anthropic-ai/vertex-sdk": "^0.14.4",
19
  "@aws-sdk/client-s3": "^3.1021.0",
20
  "@aws-sdk/s3-request-presigner": "^3.1021.0",
21
  "@hookform/resolvers": "^3.10.0",
 
62
  "express": "^4.21.2",
63
  "express-session": "^1.18.1",
64
  "framer-motion": "^11.13.1",
65
+ "google-auth-library": "^10.6.2",
66
  "input-otp": "^1.4.2",
67
  "ioredis": "^5.10.1",
68
  "lucide-react": "^0.453.0",
src/lib/ai/claude-client.ts ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Claude Client Factory β€” supports both Anthropic API and GCP Vertex AI.
3
+ *
4
+ * Auth priority (mirrors Python claude_engine.py):
5
+ * 1. ANTHROPIC_API_KEY β†’ direct Anthropic API
6
+ * 2. GCP_CREDENTIALS_JSON_B64 β†’ Vertex AI with service account
7
+ * 3. GCP ADC (Application Default Credentials) β†’ Vertex AI
8
+ */
9
+
10
+ import Anthropic from "@anthropic-ai/sdk";
11
+ import AnthropicVertex from "@anthropic-ai/vertex-sdk";
12
+
13
+ let cachedClient: Anthropic | AnthropicVertex | null = null;
14
+ let cachedAuthMethod: string = "";
15
+
16
+ export function getClaudeClient(): { client: Anthropic | AnthropicVertex; authMethod: string } {
17
+ if (cachedClient) return { client: cachedClient, authMethod: cachedAuthMethod };
18
+
19
+ // 1. Direct Anthropic API key
20
+ const apiKey = process.env.ANTHROPIC_API_KEY;
21
+ if (apiKey) {
22
+ console.log("[Claude] Auth: using ANTHROPIC_API_KEY (direct API)");
23
+ cachedClient = new Anthropic({ apiKey });
24
+ cachedAuthMethod = "anthropic_api_key";
25
+ return { client: cachedClient, authMethod: cachedAuthMethod };
26
+ }
27
+
28
+ // 2. GCP service account (base64-encoded JSON)
29
+ const credsB64 = process.env.GCP_CREDENTIALS_JSON_B64;
30
+ const project = process.env.GCP_PROJECT_ID || process.env.GOOGLE_CLOUD_PROJECT || "";
31
+ const region = process.env.GCP_LOCATION || process.env.GOOGLE_CLOUD_LOCATION || "us-east5";
32
+
33
+ if (credsB64) {
34
+ const credsJson = JSON.parse(Buffer.from(credsB64, "base64").toString("utf-8"));
35
+ const projectId = project || credsJson.project_id || "";
36
+
37
+ // AnthropicVertex uses Google ADC or explicit credentials
38
+ // Set GOOGLE_APPLICATION_CREDENTIALS via the decoded JSON
39
+ const fs = require("fs");
40
+ const tmpPath = "/tmp/gcp-creds.json";
41
+ fs.writeFileSync(tmpPath, JSON.stringify(credsJson));
42
+ process.env.GOOGLE_APPLICATION_CREDENTIALS = tmpPath;
43
+
44
+ console.log(`[Claude] Auth: using GCP service account (Vertex AI, project=${projectId}, region=${region})`);
45
+ cachedClient = new AnthropicVertex({ projectId, region });
46
+ cachedAuthMethod = "gcp_service_account";
47
+ return { client: cachedClient, authMethod: cachedAuthMethod };
48
+ }
49
+
50
+ // 3. GCP ADC (Application Default Credentials)
51
+ if (project) {
52
+ console.log(`[Claude] Auth: using GCP ADC (Vertex AI, project=${project}, region=${region})`);
53
+ cachedClient = new AnthropicVertex({ projectId: project, region });
54
+ cachedAuthMethod = "gcp_adc";
55
+ return { client: cachedClient, authMethod: cachedAuthMethod };
56
+ }
57
+
58
+ // Fallback β€” try direct API without key (will fail at call time with clear error)
59
+ console.warn("[Claude] No auth configured. Set ANTHROPIC_API_KEY or GCP_CREDENTIALS_JSON_B64.");
60
+ cachedClient = new Anthropic();
61
+ cachedAuthMethod = "none";
62
+ return { client: cachedClient, authMethod: cachedAuthMethod };
63
+ }
src/lib/editor/nli/engine.ts CHANGED
@@ -3,7 +3,7 @@
3
  * receives structured tool calls back.
4
  */
5
 
6
- import Anthropic from "@anthropic-ai/sdk";
7
  import { NLI_TOOLS } from "./tools";
8
  import type { SubtitleData } from "../event-bus";
9
 
@@ -51,7 +51,7 @@ function formatSubtitlesForContext(subtitles: SubtitleData[]): string {
51
  export async function planNliExecution(
52
  request: NliRequest,
53
  ): Promise<NliResult> {
54
- const client = new Anthropic();
55
 
56
  const contextParts: string[] = [
57
  `FPS: ${request.fps}`,
 
3
  * receives structured tool calls back.
4
  */
5
 
6
+ import { getClaudeClient } from "../../ai/claude-client";
7
  import { NLI_TOOLS } from "./tools";
8
  import type { SubtitleData } from "../event-bus";
9
 
 
51
  export async function planNliExecution(
52
  request: NliRequest,
53
  ): Promise<NliResult> {
54
+ const { client } = getClaudeClient();
55
 
56
  const contextParts: string[] = [
57
  `FPS: ${request.fps}`,
src/lib/editor/rationale-validator.ts CHANGED
@@ -3,7 +3,7 @@
3
  * Catches: empty strings, random characters, "none", "n/a", "asdf", non-English, lazy input.
4
  */
5
 
6
- import Anthropic from "@anthropic-ai/sdk";
7
 
8
  export interface RationaleEntry {
9
  index: number;
@@ -80,7 +80,7 @@ export async function validateRationales(
80
 
81
  // AI check with Haiku for the rest
82
  try {
83
- const client = new Anthropic();
84
  const prompt = needsAiCheck
85
  .map((e) => `Index ${e.index}: "${e.rationale}"`)
86
  .join("\n");
@@ -93,8 +93,8 @@ export async function validateRationales(
93
  });
94
 
95
  const text = response.content
96
- .filter((b): b is Anthropic.TextBlock => b.type === "text")
97
- .map((b) => b.text)
98
  .join("");
99
 
100
  const jsonMatch = text.match(/\{[\s\S]*\}/);
 
3
  * Catches: empty strings, random characters, "none", "n/a", "asdf", non-English, lazy input.
4
  */
5
 
6
+ import { getClaudeClient } from "../ai/claude-client";
7
 
8
  export interface RationaleEntry {
9
  index: number;
 
80
 
81
  // AI check with Haiku for the rest
82
  try {
83
+ const { client } = getClaudeClient();
84
  const prompt = needsAiCheck
85
  .map((e) => `Index ${e.index}: "${e.rationale}"`)
86
  .join("\n");
 
93
  });
94
 
95
  const text = response.content
96
+ .filter((b: any) => b.type === "text")
97
+ .map((b: any) => b.text)
98
  .join("");
99
 
100
  const jsonMatch = text.match(/\{[\s\S]*\}/);
src/lib/qc/engine.ts CHANGED
@@ -3,7 +3,8 @@
3
  * IDENTICAL port from Python qc-report-generator/src/qc/checker.py + src/engines/claude_engine.py
4
  */
5
 
6
- import Anthropic from "@anthropic-ai/sdk";
 
7
  import type { QCIssue, QCLine, QCModule, QCSeverity, QCSummary, QCResult } from "./types";
8
  import { runRuleChecks } from "./rule-checks";
9
  import { runNetflixChecks } from "./netflix-checks";
@@ -69,7 +70,7 @@ function backoffSeconds(attempt: number, isRateLimit: boolean): number {
69
  }
70
 
71
  async function callWithRetry(
72
- client: Anthropic,
73
  system: string,
74
  user: string,
75
  label: string,
@@ -85,8 +86,8 @@ async function callWithRetry(
85
  messages: [{ role: "user", content: user }],
86
  });
87
  const text = response.content
88
- .filter((b): b is Anthropic.TextBlock => b.type === "text")
89
- .map((b) => b.text)
90
  .join("");
91
  const dur = ((Date.now() - t0) / 1000).toFixed(1);
92
  log(`${label} β€” ${dur}s, prompt ${user.length} chars β†’ response ${text.length} chars`);
@@ -194,7 +195,7 @@ export async function runQCPipeline(
194
  }
195
 
196
  log(`Pipeline start: ${filename}, ${lines.length} lines, module=${module}, lang=${language}, type=${deliverableType}`);
197
- const client = new Anthropic();
198
  const { rules, systemExtra } = getModuleConfig(module);
199
 
200
  // ─── Stage 1: AI Batch QC ───
 
3
  * IDENTICAL port from Python qc-report-generator/src/qc/checker.py + src/engines/claude_engine.py
4
  */
5
 
6
+ import type Anthropic from "@anthropic-ai/sdk";
7
+ import { getClaudeClient } from "../ai/claude-client";
8
  import type { QCIssue, QCLine, QCModule, QCSeverity, QCSummary, QCResult } from "./types";
9
  import { runRuleChecks } from "./rule-checks";
10
  import { runNetflixChecks } from "./netflix-checks";
 
70
  }
71
 
72
  async function callWithRetry(
73
+ client: any, // Anthropic | AnthropicVertex β€” both have .messages.create()
74
  system: string,
75
  user: string,
76
  label: string,
 
86
  messages: [{ role: "user", content: user }],
87
  });
88
  const text = response.content
89
+ .filter((b: any) => b.type === "text")
90
+ .map((b: any) => b.text)
91
  .join("");
92
  const dur = ((Date.now() - t0) / 1000).toFixed(1);
93
  log(`${label} β€” ${dur}s, prompt ${user.length} chars β†’ response ${text.length} chars`);
 
195
  }
196
 
197
  log(`Pipeline start: ${filename}, ${lines.length} lines, module=${module}, lang=${language}, type=${deliverableType}`);
198
+ const { client } = getClaudeClient();
199
  const { rules, systemExtra } = getModuleConfig(module);
200
 
201
  // ─── Stage 1: AI Batch QC ───
tsconfig.tsbuildinfo CHANGED
The diff for this file is too large to render. See raw diff