Add image model (regolo-qwen-image) to modelSpecs, enabling UI image‑generation button

This commit is contained in:
Nathan Schneider committed 2026-10-18 16:51:36 -06:00
commit b2badebad6
4 files changed
+283

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
FROM cloudron/ai.librechat.cloudronapp:202610150224320000
# Copy custom configuration and patches into the image.
COPY librechat.yaml /app/data/librechat.yaml
COPY patches/keyBroker.js /app/code/librechat/api/server/controllers/auth/keyBroker.js
COPY patches/oauth.js /app/code/librechat/api/server/controllers/auth/oauth.js
RUN chmod 644 /app/data/librechat.yaml \
&& chmod 644 /app/code/librechat/api/server/controllers/auth/keyBroker.js \
&& chmod 644 /app/code/librechat/api/server/controllers/auth/oauth.js
+32
View File
@@ -0,0 +1,32 @@
# LibreChat sandbox maintenance
**Current image**
- Docker image: `cloudron/ai.librechat.cloudronapp:202610150224320000`
- Deployed as app **0752e430‑aa36‑436e‑8668‑ef18da474f47** (LibreChat sandbox at `https://librechat-test.inference.coop`).
**Automatic updates**
- The "Automatic updates" toggle in the Cloudron UI has been turned **off** (as of 2026‑10‑18). This prevents the sandbox from pulling newer Docker images automatically.
**Version‑pinning workflow**
1. **Create a version tag** when a new LibreChat release is approved (e.g.
`cloudron/ai.librechat.cloudronapp:202610250100000000`).
2. **Push the tagged image** to the co‑op registry (e.g. `registry.inference.coop/librechat:sandbox‑2026‑10‑25`).
```bash
docker pull cloudron/ai.librechat.cloudronapp:<new‑tag>
docker tag cloudron/ai.librechat.cloudronapp:<new‑tag> registry.inference.coop/librechat:sandbox‑<date>
docker login registry.inference.coop # use registry‑bot credentials
docker push registry.inference.coop/librechat:sandbox‑<date>
```
3. **Redeploy** the sandbox with the pinned tag:
```bash
cloudron update --app 0752e430-aa36-436e-8668-ef18da474f47 \
--image registry.inference.coop/librechat:sandbox‑<date>
```
4. **Verify** the app is running (`cloudron status --app <id>`) and that the image field reflects the new tag.
5. **Record** the change in this document (date, tag, reason) and optionally open a GitHub/Gitea issue linking to the upstream LibreChat release.
**Rollback**
- To roll back, repeat step 3 with the previous image tag.
**Future maintainers**
- All steps are captured in this doc and in the corresponding issue in `co‑op/support` (Issue #24). The `co‑op/docs` repository should contain this file; keep it up‑to‑date whenever the sandbox image changes or the automatic‑update flag is toggled.
+165
View File
@@ -0,0 +1,165 @@
version: 1.3.0
# Voice: server-side STT/TTS via the co-op gateway (Tinfoil TEE).
# Server STT works in every browser (Firefox has no Web Speech API).
speech:
stt:
openai:
url: "https://gateway.inference.coop/v1/audio/transcriptions"
apiKey: "${GATEWAY_KEY}"
model: "tinfoil/whisper-large-v3-turbo"
tts:
openai:
url: "https://gateway.inference.coop/v1/audio/speech"
apiKey: "${GATEWAY_KEY}"
model: "tinfoil/voxtral-tts"
voices: ["neutral_female", "neutral_male", "casual_female", "casual_male", "cheerful_female"]
cache: true
# Inference Co-op gateway — PER-USER keys (production pattern): no server-side
# apiKey here, so each user pastes their own dashboard key once. Usage then
# bills to that member's own LiteLLM team/budget, exactly like the API path.
endpoints:
custom:
- name: "Inference Co-op"
iconURL: "/assets/logo-coop.png"
apiKey: "user_provided"
baseURL: "https://gateway.inference.coop/v1"
models:
fetch: true
default:
- greenpt/deepseek-v4.1-flash
- tinfoil/deepseek-v4-1-flash
- tinfoil/gpt-oss-120b
- tinfoil/glm-5.3
- greenpt/glm-5.3-flash
- greenpt/glm-5.3
- regolo/apertus-70b
- regolo/qwen3.5-122b
titleConvo: true
titleModel: "greenpt/glm-5.3-flash"
titleMethod: "completion"
# Web search via the co-op's self-hosted SearXNG (public URL, JSON enabled for OWUI already).
# scraperProvider keenable = keyless page-fetch, so members get one-click search
# with no Firecrawl prompt. (Production note: keenable is an external keyless
# service — if that offends self-host values, swap in a self-hosted Firecrawl.)
webSearch:
searchProvider: "searxng"
scraperProvider: "keenable"
searxngInstanceUrl: "${SEARXNG_INSTANCE_URL}"
searxngSearchOptions:
engines: "google,bing,duckduckgo"
language: "en"
timeout: 15000
# MCP: the co-op docs, opt-in per conversation (stdlib stdio server in /app/data).
mcpServers:
inference-docs:
type: stdio
command: python3
args:
- /app/data/inference-docs-mcp/server.py
# Emoji-ified model picker (same feature-tier convention as models.md / OWUI):
# 🌱 = renewable-powered (GreenPT + Regolo), 🔒 = TEE-architected private (Tinfoil).
# Utility models (audio STT/TTS, image generation) are excluded from chat.
modelSpecs:
enforce: true
prioritize: true
list:
- name: tinfoil-deepseek-v4-1-flash
iconURL: "/assets/logo-coop.png"
label: "🔒 tinfoil/deepseek-v4-1-flash"
softDefault: true
preset:
endpoint: "Inference Co-op"
model: "tinfoil/deepseek-v4-1-flash"
- name: tinfoil-gpt-oss-120b
iconURL: "/assets/logo-coop.png"
label: "🔒 tinfoil/gpt-oss-120b"
preset:
endpoint: "Inference Co-op"
model: "tinfoil/gpt-oss-120b"
- name: tinfoil-glm-5-3
iconURL: "/assets/logo-coop.png"
label: "🔒 tinfoil/glm-5.3"
preset:
endpoint: "Inference Co-op"
model: "tinfoil/glm-5.3"
- name: greenpt-deepseek-v4-1-flash
iconURL: "/assets/logo-coop.png"
label: "🌱 greenpt/deepseek-v4.1-flash"
preset:
endpoint: "Inference Co-op"
model: "greenpt/deepseek-v4.1-flash"
- name: greenpt-glm-5-3-flash
iconURL: "/assets/logo-coop.png"
label: "🌱 greenpt/glm-5.3-flash"
preset:
endpoint: "Inference Co-op"
model: "greenpt/glm-5.3-flash"
- name: greenpt-glm-5-3
iconURL: "/assets/logo-coop.png"
label: "🌱 greenpt/glm-5.3"
preset:
endpoint: "Inference Co-op"
model: "greenpt/glm-5.3"
- name: regolo-apertus-70b
iconURL: "/assets/logo-coop.png"
label: "🌱 regolo/apertus-70b"
preset:
endpoint: "Inference Co-op"
model: "regolo/apertus-70b"
- name: regolo-qwen3-5-122b
iconURL: "/assets/logo-coop.png"
label: "🌱 regolo/qwen3.5-122b"
preset:
endpoint: "Inference Co-op"
model: "regolo/qwen3.5-122b"
- name: regolo-qwen-image
iconURL: "/assets/logo-coop.png"
label: "🖼️ regolo/qwen-image"
preset:
endpoint: "Inference Co-op"
model: "regolo/qwen-image"
# Inference Cooperative branding
interface:
customWelcome: "Welcome to your co-op AI."
theme:
version: 1
name: inference
modes:
light:
colors:
rgb-surface-primary: '250 248 245'
rgb-surface-secondary: '241 237 230'
rgb-surface-tertiary: '232 227 217'
rgb-surface-hover: '236 234 217'
rgb-surface-submit: '91 140 90'
rgb-surface-submit-hover: '74 122 74'
rgb-accent-primary: '91 140 90'
rgb-accent-primary-hover: '74 122 74'
rgb-ring-primary: '91 140 90'
rgb-link: '74 122 74'
rgb-text-primary: '36 48 32'
rgb-text-secondary: '90 107 82'
rgb-border-light: '213 221 208'
dark:
colors:
rgb-surface-primary: '31 36 27'
rgb-surface-secondary: '38 45 34'
rgb-surface-tertiary: '46 54 40'
rgb-surface-hover: '49 57 45'
rgb-surface-submit: '122 184 122'
rgb-surface-submit-hover: '142 200 142'
rgb-accent-primary: '122 184 122'
rgb-accent-primary-hover: '142 200 142'
rgb-ring-primary: '142 200 142'
rgb-link: '142 200 142'
rgb-text-primary: '236 233 226'
rgb-text-secondary: '168 173 158'
rgb-border-light: '74 82 64'
+76
View File
@@ -0,0 +1,76 @@
/**
* Key broker sync — automatically provision a member's gateway key at SSO login.
*
* Inference Cooperative patch (MIT, same as LibreChat). When KEY_BROKER_URL is
* set, an OpenID (Cloudron SSO) login fetches-or-creates the member's chat key
* via the member portal's broker and stores it as the user's LibreChat key for
* the configured endpoint — so members never paste a key and all usage bills
* to their own LiteLLM team/budget.
*
* Env:
* KEY_BROKER_URL e.g. https://portal.inference.coop
* KEY_BROKER_SECRET the portal's BROKER_SECRET (x-broker-secret header)
* KEY_BROKER_KEY_NAME portal-side key name, default "chat"
* KEY_BROKER_ENDPOINT LibreChat endpoint (user-key) name, default "Inference Co-op"
*
* Semantics: create-on-first-login. If the member already has a portal-side
* "chat" key but LibreChat lost its stored copy, we deliberately do NOT rotate
* (rotation would silently break the member's own API scripts); they can
* rotate from the dashboard and log in again.
*/
const { updateUserKey, getUserKeyExpiry } = require('~/models');
const { logger } = require('@librechat/data-schemas');
async function syncBrokerUserKey(user) {
const base = process.env.KEY_BROKER_URL;
const secret = process.env.KEY_BROKER_SECRET;
const userId = user?._id?.toString?.() || user?.id;
if (!base || !secret || !userId || !user?.email) return;
const keyName = process.env.KEY_BROKER_KEY_NAME || 'chat';
const endpointName = process.env.KEY_BROKER_ENDPOINT || 'Inference Co-op';
const headers = {
'x-broker-secret': secret,
'x-member-email': user.email,
};
// Does the member already have a portal-side chat key?
const listRes = await fetch(`${base}/broker/keys`, { headers });
if (!listRes.ok) throw new Error(`broker list ${listRes.status}`);
const list = await listRes.json();
const exists = (list.keys || []).some((k) => k.name === keyName);
if (!exists) {
const createRes = await fetch(`${base}/broker/keys`, {
method: 'POST',
headers: { ...headers, 'content-type': 'application/json' },
body: JSON.stringify({ name: keyName }),
});
if (!createRes.ok) throw new Error(`broker create ${createRes.status}`);
const created = await createRes.json();
if (!created.sk_token) throw new Error('broker returned no sk_token');
// Store the key in LibreChat (JSON string as required by LibreChat storage)
await updateUserKey({
userId: userId,
name: endpointName,
value: JSON.stringify({ apiKey: created.sk_token }),
expiresAt: null,
});
logger.info(`[KeyBroker] provisioned "${keyName}" key for ${user.email} (endpoint: ${endpointName})`);
return;
}
// Portal-side key exists — check whether LibreChat still holds a stored copy.
try {
const stored = await getUserKeyExpiry({ userId: user.id, name: endpointName });
if (!stored) {
logger.warn(
`[KeyBroker] ${user.email} has a portal "${keyName}" key but no stored copy for endpoint "${endpointName}". ` +
'Rotate the key on the dashboard and log in again (manual rotation avoids breaking their API scripts).',
);
}
} catch (e) {
logger.warn(`[KeyBroker] stored-key check failed for ${user.email}: ${e.message}`);
}
}
module.exports = { syncBrokerUserKey };