@r8s/librechat 0.2.0 → 0.2.1

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.
@@ -1,545 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { render, jsx } from '@r8s/core'
3
- import { OperatorContext, SecretContext, RoutingContext } from '@r8s/core/defaults'
4
- import { runGuardrails, noPlaintextSecrets, validateResource } from '@r8s/core'
5
- import { operators } from '@r8s/crds'
6
- import type { r8sElement } from '@r8s/core'
7
-
8
- // LibreChat recipe tests:
9
- // 1. Operator declarations (deduped via OperatorContext)
10
- // 2. Rendering: defaults, Meilisearch, all props, gateway/ingress adaptation
11
- // 3. MongoDB wiring: MONGO_URI expansion + secretKeyRef credentials
12
- // 4. Env wiring: reverse proxy, redis, search, session credentials,
13
- // upstream env names, no duplicate env names
14
- // 5. Security: no plaintext credentials in rendered output
15
- import { LibreChat } from '../src/index'
16
-
17
- const openbao = { backend: 'openbao', mount: 'kv', path: 'test' }
18
-
19
- const mongodb = {
20
- host: 'mongo.data.svc.cluster.local',
21
- port: 27017,
22
- passwordSecret: 'chat-mongodb-credentials',
23
- }
24
-
25
- const sso = {
26
- issuer: 'https://keycloak.example.com/realms/platform',
27
- clientId: 'librechat',
28
- clientSecretRef: { secret: 'librechat-sso', key: 'clientSecret' },
29
- }
30
-
31
- /** Render LibreChat inside a Platform-like secrets backend (OpenBao). */
32
- function renderLibreChat(props: Record<string, unknown>): ReturnType<typeof render> {
33
- return render(
34
- jsx(SecretContext.Provider, {
35
- value: openbao as never,
36
- children: jsx(LibreChat, props as never),
37
- })
38
- )
39
- }
40
-
41
- /** Wrap LibreChat in an OperatorContext (no secrets backend). */
42
- function elementWithContext(ops: any[], props: Record<string, unknown>): r8sElement {
43
- return jsx(OperatorContext.Provider, {
44
- value: ops,
45
- children: jsx(LibreChat, props as never),
46
- })
47
- }
48
-
49
- /**
50
- * k8s rejects duplicate env names in one container — assert every env
51
- * list we render is unique (this bit us with `$(VAR)` self-echoes of
52
- * secretKeyRef-backed vars).
53
- */
54
- function assertUniqueEnvNames(env: Array<{ name: string }>): void {
55
- const names = env.map((e) => e.name)
56
- const dupes = names.filter((n, i) => names.indexOf(n) !== i)
57
- expect(dupes).toEqual([])
58
- expect(new Set(names).size).toBe(names.length)
59
- }
60
-
61
- describe('operator declarations', () => {
62
- it('declares the redis operator when cache is enabled', () => {
63
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
64
- expect(result.operators.some((op) => op.name === 'redis-operator')).toBe(true)
65
- })
66
-
67
- it('skips the redis operator when cache is disabled', () => {
68
- const result = renderLibreChat({ host: 'chat.example.com', mongodb, cache: false })
69
- expect(result.operators.some((op) => op.name === 'redis-operator')).toBe(false)
70
- })
71
-
72
- it('never declares cnpg (MongoDB is provisioned externally)', () => {
73
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
74
- expect(result.operators.some((op) => op.name === 'cnpg')).toBe(false)
75
- })
76
-
77
- it('deduplicates operators provided via context', () => {
78
- const result = render(
79
- elementWithContext([operators['redis-operator']()], {
80
- host: 'chat.example.com',
81
- mongodb,
82
- secretsName: 'existing-secrets',
83
- })
84
- )
85
- const names = result.operators.map((op) => op.name)
86
- expect(names.filter((n) => n === 'redis-operator')).toHaveLength(1)
87
- })
88
-
89
- it('allows version overrides through context operators', () => {
90
- const result = render(
91
- elementWithContext([operators['redis-operator']('1.0.0')], {
92
- host: 'chat.example.com',
93
- mongodb,
94
- secretsName: 'existing-secrets',
95
- })
96
- )
97
- const redis = result.operators.filter((op) => op.name === 'redis-operator')
98
- expect(redis).toHaveLength(1)
99
- expect(redis[0].version).toBe('1.0.0')
100
- })
101
- })
102
-
103
- describe('rendering defaults', () => {
104
- it('renders app deployment, service, ingress and redis', () => {
105
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
106
- const kinds = result.resources.map((r) => r.kind)
107
- expect(kinds).toContain('Deployment')
108
- expect(kinds).toContain('Service')
109
- expect(kinds).toContain('Ingress')
110
- expect(kinds).toContain('RedisReplication')
111
- expect(kinds).not.toContain('RedisCluster')
112
- expect(kinds).toContain('OpenBaoStaticSecret')
113
- })
114
-
115
- it('renders a Meilisearch deployment with health probes and resources when search is enabled', () => {
116
- const result = renderLibreChat({ host: 'chat.example.com', mongodb, search: true })
117
- const deployments = result.resources.filter((r) => r.kind === 'Deployment')
118
- const services = result.resources.filter((r) => r.kind === 'Service')
119
- expect(deployments.map((d: any) => d.metadata.name)).toContain('librechat-meilisearch')
120
- expect(services.map((s: any) => s.metadata.name)).toContain('librechat-meilisearch')
121
- const meili = deployments.find((d: any) => d.metadata.name === 'librechat-meilisearch') as any
122
- expect(meili.spec.template.spec.containers[0].image).toBe('getmeili/meilisearch:v1.6')
123
- const meiliContainer = meili.spec.template.spec.containers[0]
124
- expect(meiliContainer.livenessProbe.httpGet).toEqual({ path: '/health', port: 7700 })
125
- expect(meiliContainer.readinessProbe.httpGet).toEqual({ path: '/health', port: 7700 })
126
- expect(meiliContainer.resources).toEqual({
127
- requests: { memory: '256Mi', cpu: '100m' },
128
- limits: { memory: '1Gi', cpu: '500m' },
129
- })
130
- assertUniqueEnvNames(meiliContainer.env)
131
- })
132
-
133
- it('renders no Meilisearch resources and no SEARCH flag when search is disabled', () => {
134
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
135
- const names = result.resources
136
- .filter((r) => r.kind === 'Deployment' || r.kind === 'Service')
137
- .map((r: any) => r.metadata.name)
138
- expect(names).not.toContain('librechat-meilisearch')
139
- const app = result.resources.find(
140
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
141
- ) as any
142
- const env = app.spec.template.spec.containers[0].env
143
- expect(env.find((e: any) => e.name === 'SEARCH')).toBeUndefined()
144
- })
145
-
146
- it('quotes the redis master service and enables USE_REDIS', () => {
147
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
148
- const redis = result.resources.find((r) => r.kind === 'RedisReplication') as any
149
- expect(redis.metadata.name).toBe('librechat-redis')
150
- expect(redis.spec.clusterSize).toBe(3)
151
- expect(redis.spec.kubernetesConfig.image).toBe('redis:7.2-alpine')
152
-
153
- const app = result.resources.find(
154
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
155
- ) as any
156
- const env = app.spec.template.spec.containers[0].env
157
- expect(env.find((e: any) => e.name === 'USE_REDIS').value).toBe('true')
158
- expect(env.find((e: any) => e.name === 'REDIS_URI').value).toBe('redis://librechat-redis:6379')
159
- })
160
-
161
- it('renders gateway resources when platform uses gateway routing', () => {
162
- const result = render(
163
- jsx(RoutingContext.Provider, {
164
- value: { mode: 'gateway', gatewayClassName: 'eg' },
165
- children: jsx(SecretContext.Provider, {
166
- value: openbao as never,
167
- children: jsx(LibreChat, { host: 'chat.example.com', mongodb }),
168
- }),
169
- })
170
- )
171
- const kinds = result.resources.map((r) => r.kind)
172
- expect(kinds).toContain('HTTPRoute')
173
- })
174
-
175
- it('renders a valid Ingress when platform uses ingress routing', () => {
176
- const result = render(
177
- jsx(RoutingContext.Provider, {
178
- value: { mode: 'ingress' },
179
- children: jsx(SecretContext.Provider, {
180
- value: openbao as never,
181
- children: jsx(LibreChat, { host: 'chat.example.com', mongodb }),
182
- }),
183
- })
184
- )
185
- const ingress = result.resources.find((r) => r.kind === 'Ingress') as any
186
- expect(ingress).toBeDefined()
187
- expect(ingress.spec.rules[0].host).toBe('chat.example.com')
188
- })
189
-
190
- it('sets websocket-friendly proxy annotations on the Ingress', () => {
191
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
192
- const ingress = result.resources.find((r) => r.kind === 'Ingress') as any
193
- expect(ingress.metadata.annotations).toMatchObject({
194
- 'nginx.ingress.kubernetes.io/proxy-read-timeout': '300',
195
- 'nginx.ingress.kubernetes.io/proxy-send-timeout': '300',
196
- 'nginx.ingress.kubernetes.io/proxy-buffering': 'off',
197
- })
198
- })
199
-
200
- it('passes resource validation', () => {
201
- const result = renderLibreChat({
202
- host: 'chat.example.com',
203
- mongodb,
204
- search: true,
205
- sso,
206
- })
207
- for (const resource of result.resources) {
208
- expect(validateResource(resource)).toEqual([])
209
- }
210
- })
211
- })
212
-
213
- describe('rendering with all props', () => {
214
- it('accepts the full prop surface', () => {
215
- const result = renderLibreChat({
216
- name: 'chat',
217
- namespace: 'chat',
218
- version: '0.7.8',
219
- host: 'chat.example.com',
220
- port: 3080,
221
- replicas: 3,
222
- mongodb: { ...mongodb, username: 'chat' },
223
- cache: true,
224
- search: true,
225
- sso,
226
- backend: 'https://api.berget.ai/v1',
227
- resources: {
228
- requests: { memory: '1Gi', cpu: '500m' },
229
- limits: { memory: '4Gi', cpu: '2000m' },
230
- },
231
- tls: { secretName: 'chat-tls', clusterIssuer: 'letsencrypt-prod' },
232
- })
233
-
234
- const app = result.resources.find(
235
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'chat'
236
- ) as any
237
- expect(app).toBeDefined()
238
- expect(app.spec.replicas).toBe(3)
239
- expect(app.spec.template.spec.containers[0].image).toBe('ghcr.io/danny-avila/librechat:0.7.8')
240
- expect(app.spec.template.spec.containers[0].resources.limits.memory).toBe('4Gi')
241
- expect(app.spec.template.spec.containers[0].ports[0].containerPort).toBe(3080)
242
- assertUniqueEnvNames(app.spec.template.spec.containers[0].env)
243
- })
244
- })
245
-
246
- describe('mongodb wiring', () => {
247
- it('builds MONGO_URI with $(VAR) expansion and the database named after the resource', () => {
248
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
249
- const app = result.resources.find(
250
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
251
- ) as any
252
- const env = app.spec.template.spec.containers[0].env
253
- const mongoUri = env.find((e: any) => e.name === 'MONGO_URI')
254
- expect(mongoUri.value).toBe(
255
- 'mongodb://$(MONGO_USERNAME):$(MONGO_PASSWORD)@mongo.data.svc.cluster.local:27017/librechat'
256
- )
257
- })
258
-
259
- it('appends authSource when the mongodb connection declares one', () => {
260
- const result = renderLibreChat({
261
- host: 'chat.example.com',
262
- mongodb: { ...mongodb, authSource: 'admin' },
263
- })
264
- const app = result.resources.find(
265
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
266
- ) as any
267
- const env = app.spec.template.spec.containers[0].env
268
- expect(env.find((e: any) => e.name === 'MONGO_URI').value).toBe(
269
- 'mongodb://$(MONGO_USERNAME):$(MONGO_PASSWORD)@mongo.data.svc.cluster.local:27017/librechat?authSource=admin'
270
- )
271
- })
272
-
273
- it('delivers MONGO_USERNAME / MONGO_PASSWORD via secretKeyRef from the password secret', () => {
274
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
275
- const app = result.resources.find(
276
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
277
- ) as any
278
- const env = app.spec.template.spec.containers[0].env
279
- const username = env.find((e: any) => e.name === 'MONGO_USERNAME')
280
- const password = env.find((e: any) => e.name === 'MONGO_PASSWORD')
281
- expect(username.valueFrom.secretKeyRef).toEqual({
282
- name: 'chat-mongodb-credentials',
283
- key: 'username',
284
- })
285
- expect(password.valueFrom.secretKeyRef).toEqual({
286
- name: 'chat-mongodb-credentials',
287
- key: 'password',
288
- })
289
- expect(username.value).toBeUndefined()
290
- expect(password.value).toBeUndefined()
291
- })
292
-
293
- it('inlines an explicitly provided username (identifier, not secret)', () => {
294
- const result = renderLibreChat({
295
- host: 'chat.example.com',
296
- mongodb: { ...mongodb, username: 'chat' },
297
- })
298
- const app = result.resources.find(
299
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
300
- ) as any
301
- const env = app.spec.template.spec.containers[0].env
302
- const username = env.find((e: any) => e.name === 'MONGO_USERNAME')
303
- const password = env.find((e: any) => e.name === 'MONGO_PASSWORD')
304
- expect(username.value).toBe('chat')
305
- expect(username.valueFrom).toBeUndefined()
306
- expect(password.valueFrom.secretKeyRef.key).toBe('password')
307
- })
308
-
309
- it('shares MEILI_MASTER_KEY with the main app via the secrets bundle when search is on', () => {
310
- const result = renderLibreChat({ host: 'chat.example.com', mongodb, search: true })
311
- const app = result.resources.find(
312
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
313
- ) as any
314
- const env = app.spec.template.spec.containers[0].env
315
- expect(env.find((e: any) => e.name === 'SEARCH').value).toBe('true')
316
- expect(env.find((e: any) => e.name === 'MEILI_HOST').value).toBe(
317
- 'http://librechat-meilisearch:7700'
318
- )
319
- const meiliKey = env.find((e: any) => e.name === 'MEILI_MASTER_KEY')
320
- expect(meiliKey.valueFrom.secretKeyRef.name).toBe('librechat-secrets')
321
- expect(meiliKey.valueFrom.secretKeyRef.key).toBe('meiliMasterKey')
322
- expect(meiliKey.value).toBeUndefined()
323
- assertUniqueEnvNames(env)
324
-
325
- const meiliDeployment = result.resources.find(
326
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat-meilisearch'
327
- ) as any
328
- const meiliEnv = meiliDeployment.spec.template.spec.containers[0].env
329
- const meiliMasterKey = meiliEnv.find((e: any) => e.name === 'MEILI_MASTER_KEY')
330
- expect(meiliMasterKey.valueFrom.secretKeyRef.name).toBe('librechat-secrets')
331
- expect(meiliMasterKey.value).toBeUndefined()
332
- })
333
- })
334
-
335
- describe('session credentials', () => {
336
- it('wires JWT + CREDS multi-user credentials via secretKeyRef from the secrets bundle', () => {
337
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
338
- const app = result.resources.find(
339
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
340
- ) as any
341
- const env = app.spec.template.spec.containers[0].env
342
- const jwtSecret = env.find((e: any) => e.name === 'JWT_SECRET')
343
- const jwtRefresh = env.find((e: any) => e.name === 'JWT_REFRESH_SECRET')
344
- const credsKey = env.find((e: any) => e.name === 'CREDS_KEY')
345
- const credsIv = env.find((e: any) => e.name === 'CREDS_IV')
346
- expect(jwtSecret).toMatchObject({
347
- valueFrom: { secretKeyRef: { name: 'librechat-secrets', key: 'jwtSecret' } },
348
- })
349
- expect(jwtRefresh).toMatchObject({
350
- valueFrom: { secretKeyRef: { name: 'librechat-secrets', key: 'jwtRefreshSecret' } },
351
- })
352
- expect(credsKey).toMatchObject({
353
- valueFrom: { secretKeyRef: { name: 'librechat-secrets', key: 'credsKey' } },
354
- })
355
- expect(credsIv).toMatchObject({
356
- valueFrom: { secretKeyRef: { name: 'librechat-secrets', key: 'credsIv' } },
357
- })
358
- for (const e of [jwtSecret, jwtRefresh, credsKey, credsIv]) {
359
- expect(e.value).toBeUndefined()
360
- }
361
- // REFRESH_TOKEN_EXPIRY is a plain TTL — delivered via configMapKeyRef
362
- const refreshTokenExpiry = env.find((e: any) => e.name === 'REFRESH_TOKEN_EXPIRY')
363
- expect(refreshTokenExpiry.valueFrom.configMapKeyRef).toEqual({
364
- name: 'librechat-config',
365
- key: 'REFRESH_TOKEN_EXPIRY',
366
- })
367
- expect(refreshTokenExpiry.value).toBeUndefined()
368
- const configMap = result.resources.find(
369
- (r: any) => r.kind === 'ConfigMap' && r.metadata.name === 'librechat-config'
370
- ) as any
371
- expect(configMap.data).toEqual({ REFRESH_TOKEN_EXPIRY: '604800' })
372
- assertUniqueEnvNames(env)
373
- })
374
-
375
- it('resolves session credential keys against an explicit secretsName', () => {
376
- const result = renderLibreChat({
377
- host: 'chat.example.com',
378
- mongodb,
379
- secretsName: 'existing-secrets',
380
- })
381
- const app = result.resources.find(
382
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
383
- ) as any
384
- const env = app.spec.template.spec.containers[0].env
385
- for (const envName of ['JWT_SECRET', 'JWT_REFRESH_SECRET', 'CREDS_KEY', 'CREDS_IV']) {
386
- expect(env.find((e: any) => e.name === envName).valueFrom.secretKeyRef.name).toBe(
387
- 'existing-secrets'
388
- )
389
- }
390
- })
391
- })
392
-
393
- describe('secrets handling', () => {
394
- it('provisions the app secrets bundle through a secrets backend', () => {
395
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
396
- const bao = result.resources.find((r) => r.kind === 'OpenBaoStaticSecret') as any
397
- expect(bao).toBeDefined()
398
- expect(bao.spec.destination.name).toBe('librechat-secrets')
399
- expect(bao.spec.path).toBe('test/librechat/secrets')
400
- })
401
-
402
- it('provisions the app secrets bundle through Vault', () => {
403
- const result = render(
404
- jsx(SecretContext.Provider, {
405
- value: { backend: 'vault', mount: 'kv', path: 'apps' },
406
- children: jsx(LibreChat, { host: 'chat.example.com', mongodb }),
407
- })
408
- )
409
- const vault = result.resources.find((r) => r.kind === 'VaultStaticSecret') as any
410
- expect(vault).toBeDefined()
411
- expect(vault.spec.destination.name).toBe('librechat-secrets')
412
- expect(vault.spec.path).toBe('apps/librechat/secrets')
413
- })
414
-
415
- it('accepts an existing secretsName without a backend', () => {
416
- expect(() =>
417
- render(
418
- jsx(LibreChat, {
419
- host: 'chat.example.com',
420
- mongodb,
421
- secretsName: 'existing-secrets',
422
- })
423
- )
424
- ).not.toThrow()
425
- const result = render(
426
- jsx(LibreChat, {
427
- host: 'chat.example.com',
428
- mongodb,
429
- secretsName: 'existing-secrets',
430
- })
431
- )
432
- expect(
433
- result.resources.find(
434
- (r) => r.kind === 'OpenBaoStaticSecret' || r.kind === 'VaultStaticSecret'
435
- )
436
- ).toBeUndefined()
437
- const app = result.resources.find(
438
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
439
- ) as any
440
- const env = app.spec.template.spec.containers[0].env
441
- expect(env.find((e: any) => e.name === 'SECRET_KEY').valueFrom.secretKeyRef.name).toBe(
442
- 'existing-secrets'
443
- )
444
- })
445
-
446
- it('throws when no secrets backend and no secretsName', () => {
447
- expect(() => render(jsx(LibreChat, { host: 'chat.example.com', mongodb }))).toThrow(
448
- /application secrets/
449
- )
450
- })
451
-
452
- it('wires app credentials via secretKeyRef (never plaintext env)', () => {
453
- const result = renderLibreChat({ host: 'chat.example.com', mongodb })
454
- const app = result.resources.find(
455
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
456
- ) as any
457
- const env = app.spec.template.spec.containers[0].env
458
- const secretKey = env.find((e: any) => e.name === 'SECRET_KEY')
459
- const modelApiKey = env.find((e: any) => e.name === 'OPENAI_API_KEY')
460
- expect(secretKey.valueFrom.secretKeyRef.name).toBe('librechat-secrets')
461
- expect(secretKey.valueFrom.secretKeyRef.key).toBe('secretKey')
462
- expect(modelApiKey.valueFrom.secretKeyRef.name).toBe('librechat-secrets')
463
- expect(modelApiKey.valueFrom.secretKeyRef.key).toBe('modelApiKey')
464
- expect(secretKey.value).toBeUndefined()
465
- expect(modelApiKey.value).toBeUndefined()
466
- })
467
-
468
- it('renders no plaintext credentials anywhere', () => {
469
- const result = renderLibreChat({
470
- host: 'chat.example.com',
471
- mongodb,
472
- search: true,
473
- sso,
474
- })
475
- const { passed, errors } = runGuardrails(result.resources as any[], [noPlaintextSecrets])
476
- if (!passed) {
477
- console.error('Plaintext credential violations:', errors)
478
- }
479
- expect(passed).toBe(true)
480
- })
481
- })
482
-
483
- describe('sso wiring', () => {
484
- it('sets OPENID env vars and wires the client secret via secretKeyRef', () => {
485
- const result = renderLibreChat({ host: 'chat.example.com', mongodb, sso })
486
- const app = result.resources.find(
487
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
488
- ) as any
489
- const env = app.spec.template.spec.containers[0].env
490
- const names = env.map((e: any) => e.name)
491
- assertUniqueEnvNames(env)
492
- expect(env.find((e: any) => e.name === 'ALLOW_SOCIAL_LOGIN').value).toBe('true')
493
- expect(env.find((e: any) => e.name === 'DOMAIN_SERVER').value).toBe('https://chat.example.com')
494
- expect(env.find((e: any) => e.name === 'DOMAIN_CLIENT').value).toBe('https://chat.example.com')
495
- expect(env.find((e: any) => e.name === 'OPENID_ISSUER').value).toBe(sso.issuer)
496
- expect(env.find((e: any) => e.name === 'OPENID_CLIENT_ID').value).toBe(sso.clientId)
497
- expect(env.find((e: any) => e.name === 'OPENID_SCOPES').value).toBe('openid profile email')
498
- expect(env.find((e: any) => e.name === 'OPENID_CALLBACK_URL').value).toBe(
499
- 'https://chat.example.com/oauth/openid/callback'
500
- )
501
- const clientSecret = env.find((e: any) => e.name === 'OPENID_CLIENT_SECRET')
502
- expect(clientSecret.valueFrom.secretKeyRef).toEqual({
503
- name: 'librechat-sso',
504
- key: 'clientSecret',
505
- })
506
- expect(clientSecret.value).toBeUndefined()
507
- // no $(VAR) self-echo of the secretKeyRef-backed var
508
- expect(names).not.toContain('OPENID_SCOPE')
509
- })
510
- })
511
-
512
- describe('openai backend wiring', () => {
513
- it('routes model calls through OPENAI_REVERSE_PROXY (no OPENAI_API_BASE_URL)', () => {
514
- const result = renderLibreChat({
515
- host: 'chat.example.com',
516
- mongodb,
517
- secretsName: 'existing-secrets',
518
- })
519
- const app = result.resources.find(
520
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
521
- ) as any
522
- const env = app.spec.template.spec.containers[0].env
523
- const names = env.map((e: any) => e.name)
524
- expect(env.find((e: any) => e.name === 'OPENAI_REVERSE_PROXY').value).toBe(
525
- 'https://api.berget.ai/v1/chat/completions'
526
- )
527
- expect(names).not.toContain('OPENAI_API_BASE_URL')
528
- })
529
-
530
- it('honors a backend override with the chat/completions suffix', () => {
531
- const result = renderLibreChat({
532
- host: 'chat.example.com',
533
- mongodb,
534
- backend: 'https://api.example.com/v1',
535
- secretsName: 'existing-secrets',
536
- })
537
- const app = result.resources.find(
538
- (r: any) => r.kind === 'Deployment' && r.metadata.name === 'librechat'
539
- ) as any
540
- const env = app.spec.template.spec.containers[0].env
541
- expect(env.find((e: any) => e.name === 'OPENAI_REVERSE_PROXY').value).toBe(
542
- 'https://api.example.com/v1/chat/completions'
543
- )
544
- })
545
- })
@@ -1,17 +0,0 @@
1
- import { Platform } from '@r8s/recipes'
2
- import { LibreChat } from '@r8s/librechat'
3
-
4
- export default (
5
- <Platform secrets={{ backend: 'openbao', mount: 'kv', path: 'apps' }}>
6
- <LibreChat
7
- name="chat"
8
- host="chat.example.com"
9
- mongodb={{ host: 'mongo.data.svc.cluster.local', passwordSecret: 'chat-mongodb-credentials' }}
10
- sso={{
11
- issuer: 'https://keycloak.example.com/realms/platform',
12
- clientId: 'librechat',
13
- clientSecretRef: { secret: 'librechat-sso', key: 'clientSecret' },
14
- }}
15
- />
16
- </Platform>
17
- )