mirror of
https://github.com/nexu-io/open-design.git
synced 2026-07-07 06:34:10 +08:00
fix(web,daemon): make max_tokens configurable (closes #29)
BYOK users on custom Anthropic-compatible providers (e.g. Xiaomi MiMo) hit the hardcoded 8192 cap and saw artifacts truncated mid-stream. - AppConfig.maxTokens with Settings input (EN/CN + 8 other locales) - ProxyStreamRequest.maxTokens contract field - anthropic, anthropic-compatible, and openai-compatible providers all forward cfg.maxTokens - /api/proxy/anthropic/stream and /api/proxy/stream payloads honor it, defaulting to 8192 when unset so prior clients are unaffected Original sketch by @mashu in #78 (50a9d14); rebased to the apps/web layout and extended to the proxy paths actually used when baseUrl is set, which is where #29's user actually traffics.
This commit is contained in:
@@ -2051,7 +2051,7 @@ export async function startServer({ port = 7456, returnServer = false } = {}) {
|
||||
app.post('/api/proxy/anthropic/stream', async (req, res) => {
|
||||
/** @type {Partial<ProxyStreamRequest>} */
|
||||
const proxyBody = req.body || {};
|
||||
const { baseUrl, apiKey, model, systemPrompt, messages } = proxyBody;
|
||||
const { baseUrl, apiKey, model, systemPrompt, messages, maxTokens } = proxyBody;
|
||||
if (!baseUrl || !apiKey || !model) {
|
||||
return sendApiError(res, 400, 'BAD_REQUEST', 'baseUrl, apiKey, and model are required');
|
||||
}
|
||||
@@ -2067,7 +2067,7 @@ export async function startServer({ port = 7456, returnServer = false } = {}) {
|
||||
|
||||
const payload = {
|
||||
model,
|
||||
max_tokens: 8192,
|
||||
max_tokens: typeof maxTokens === 'number' && maxTokens > 0 ? maxTokens : 8192,
|
||||
stream: true,
|
||||
system: systemPrompt || '',
|
||||
messages: Array.isArray(messages)
|
||||
@@ -2166,7 +2166,7 @@ export async function startServer({ port = 7456, returnServer = false } = {}) {
|
||||
app.post('/api/proxy/stream', async (req, res) => {
|
||||
/** @type {Partial<ProxyStreamRequest>} */
|
||||
const proxyBody = req.body || {};
|
||||
const { baseUrl, apiKey, model, systemPrompt, messages } = proxyBody;
|
||||
const { baseUrl, apiKey, model, systemPrompt, messages, maxTokens } = proxyBody;
|
||||
if (!baseUrl || !apiKey || !model) {
|
||||
return sendApiError(res, 400, 'BAD_REQUEST', 'baseUrl, apiKey, and model are required');
|
||||
}
|
||||
@@ -2194,7 +2194,7 @@ export async function startServer({ port = 7456, returnServer = false } = {}) {
|
||||
|
||||
const payload = {
|
||||
model,
|
||||
max_tokens: 8192,
|
||||
max_tokens: typeof maxTokens === 'number' && maxTokens > 0 ? maxTokens : 8192,
|
||||
stream: true,
|
||||
...(isMiMo ? { tool_choice: 'none', tools: [] } : {}),
|
||||
messages: [
|
||||
|
||||
@@ -445,6 +445,21 @@ export function SettingsDialog({
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="field">
|
||||
<span className="field-label">{t('settings.maxTokens')}</span>
|
||||
<input
|
||||
type="number"
|
||||
min={1024}
|
||||
max={200000}
|
||||
step={1024}
|
||||
value={cfg.maxTokens ?? 8192}
|
||||
onChange={(e) => {
|
||||
const val = parseInt(e.target.value, 10);
|
||||
setCfg({ ...cfg, maxTokens: Number.isFinite(val) ? val : undefined });
|
||||
}}
|
||||
/>
|
||||
<p className="hint">{t('settings.maxTokensHint')}</p>
|
||||
</label>
|
||||
<p className="hint">{t('settings.apiHint')}</p>
|
||||
</section>
|
||||
)}
|
||||
|
||||
@@ -73,6 +73,9 @@ export const de: Dict = {
|
||||
'settings.hide': 'Ausblenden',
|
||||
'settings.model': 'Modell',
|
||||
'settings.baseUrl': 'Base URL',
|
||||
'settings.maxTokens': 'Max. Tokens',
|
||||
'settings.maxTokensHint':
|
||||
'Obergrenze für die Antwortlänge. Standard: 8192. Erhöhen (z. B. 32768), wenn ein einzelnes Design-Artefakt mitten im Stream abgeschnitten wird.',
|
||||
'settings.apiHint':
|
||||
'Aufrufe gehen direkt von diesem Browser an die festgelegte Base URL. Kein Proxy. Der Key verlässt localStorage nie.',
|
||||
'settings.skipForNow': 'Vorerst überspringen',
|
||||
|
||||
@@ -73,6 +73,9 @@ export const en: Dict = {
|
||||
'settings.hide': 'Hide',
|
||||
'settings.model': 'Model',
|
||||
'settings.baseUrl': 'Base URL',
|
||||
'settings.maxTokens': 'Max tokens',
|
||||
'settings.maxTokensHint':
|
||||
'Cap on the response length. Defaults to 8192. Raise it (e.g. 32768) when a single design artifact gets cut off mid-stream.',
|
||||
'settings.apiHint':
|
||||
'Calls go directly from this browser to the base URL you set. No proxy. The key never leaves localStorage.',
|
||||
'settings.skipForNow': 'Skip for now',
|
||||
|
||||
@@ -73,6 +73,9 @@ export const esES: Dict = {
|
||||
'settings.hide': 'Ocultar',
|
||||
'settings.model': 'Modelo',
|
||||
'settings.baseUrl': 'URL base',
|
||||
'settings.maxTokens': 'Tokens máx.',
|
||||
'settings.maxTokensHint':
|
||||
'Tope para la longitud de la respuesta. Por defecto 8192. Súbelo (p. ej. 32768) cuando un artefacto de diseño se trunca a mitad del stream.',
|
||||
'settings.apiHint':
|
||||
'Las llamadas van directamente desde este navegador a la URL base que indiques. Sin proxy. La clave nunca sale de localStorage.',
|
||||
'settings.skipForNow': 'Omitir por ahora',
|
||||
|
||||
@@ -73,6 +73,9 @@ export const fa: Dict = {
|
||||
'settings.hide': 'پنهان',
|
||||
'settings.model': 'مدل',
|
||||
'settings.baseUrl': 'آدرس پایه',
|
||||
'settings.maxTokens': 'حداکثر توکن',
|
||||
'settings.maxTokensHint':
|
||||
'سقف طول پاسخ. مقدار پیشفرض ۸۱۹۲. اگر یک artifact طراحی در میانهٔ استریم قطع میشود، آن را افزایش دهید (مثلاً ۳۲۷۶۸).',
|
||||
'settings.apiHint':
|
||||
'فراخوانیها مستقیماً از این مرورگر به آدرس پایهای که تعیین کردهاید ارسال میشوند. بدون پراکسی. کلید هرگز localStorage را ترک نمیکند.',
|
||||
'settings.skipForNow': 'فعلاً رد کنید',
|
||||
|
||||
@@ -73,6 +73,9 @@ export const ja: Dict = {
|
||||
'settings.hide': '隠す',
|
||||
'settings.model': 'モデル',
|
||||
'settings.baseUrl': 'ベース URL',
|
||||
'settings.maxTokens': '最大トークン',
|
||||
'settings.maxTokensHint':
|
||||
'応答長の上限。デフォルトは 8192。デザインの artifact がストリームの途中で切れる場合は、32768 などに引き上げてください。',
|
||||
'settings.apiHint':
|
||||
'リクエストはこのブラウザから設定したベース URL に直接送信されます。プロキシなし。キーは localStorage から外に出ません。',
|
||||
'settings.skipForNow': '今はスキップ',
|
||||
|
||||
@@ -73,6 +73,9 @@ export const ptBR: Dict = {
|
||||
'settings.hide': 'Ocultar',
|
||||
'settings.model': 'Modelo',
|
||||
'settings.baseUrl': 'URL base',
|
||||
'settings.maxTokens': 'Tokens máx.',
|
||||
'settings.maxTokensHint':
|
||||
'Limite para o comprimento da resposta. Padrão: 8192. Aumente (por exemplo, 32768) quando um artefato de design é truncado no meio do stream.',
|
||||
'settings.apiHint':
|
||||
'As chamadas vão direto deste navegador para a URL base definida. Sem proxy. A chave nunca sai do localStorage.',
|
||||
'settings.skipForNow': 'Pular por enquanto',
|
||||
|
||||
@@ -73,6 +73,9 @@ export const ru: Dict = {
|
||||
'settings.hide': 'Скрыть',
|
||||
'settings.model': 'Модель',
|
||||
'settings.baseUrl': 'Базовый URL',
|
||||
'settings.maxTokens': 'Макс. токенов',
|
||||
'settings.maxTokensHint':
|
||||
'Ограничение длины ответа. По умолчанию 8192. Увеличьте (например, до 32768), если один артефакт дизайна обрывается посреди стрима.',
|
||||
'settings.apiHint':
|
||||
'Вызовы идут напрямую из этого браузера на указанный вами базовый URL. Без прокси. Ключ никогда не покидает localStorage.',
|
||||
'settings.skipForNow': 'Пропустить сейчас',
|
||||
|
||||
@@ -73,6 +73,9 @@ export const tr: Dict = {
|
||||
'settings.hide': 'Gizle',
|
||||
'settings.model': 'Model',
|
||||
'settings.baseUrl': 'Temel URL',
|
||||
'settings.maxTokens': 'Maks. token',
|
||||
'settings.maxTokensHint':
|
||||
'Yanıt uzunluğu sınırı. Varsayılan 8192. Bir tasarım artifact akış ortasında kesildiğinde değeri yükseltin (örn. 32768).',
|
||||
'settings.apiHint':
|
||||
'Çağrılar, bu tarayıcıdan doğrudan belirlediğiniz temel URL’ye yönlendirilir. Vekil sunucu kullanılmaz. Anahtar hiçbir zaman localStorage’dan ayrılmaz.',
|
||||
'settings.skipForNow': 'Şimdilik atla',
|
||||
|
||||
@@ -72,6 +72,9 @@ export const zhCN: Dict = {
|
||||
'settings.hide': '隐藏',
|
||||
'settings.model': '模型',
|
||||
'settings.baseUrl': 'Base URL',
|
||||
'settings.maxTokens': '最大 tokens',
|
||||
'settings.maxTokensHint':
|
||||
'响应长度上限,默认 8192。当单次生成的 artifact 被截断时,可以调高(例如 32768)。',
|
||||
'settings.apiHint':
|
||||
'请求会从当前浏览器直连你设置的 Base URL,无中转代理。Key 只存放在 localStorage。',
|
||||
'settings.skipForNow': '暂时跳过',
|
||||
|
||||
@@ -72,6 +72,9 @@ export const zhTW: Dict = {
|
||||
'settings.hide': '隱藏',
|
||||
'settings.model': '模型',
|
||||
'settings.baseUrl': 'Base URL',
|
||||
'settings.maxTokens': '最大 tokens',
|
||||
'settings.maxTokensHint':
|
||||
'回應長度上限,預設 8192。當單次生成的 artifact 被截斷時,可以調高(例如 32768)。',
|
||||
'settings.apiHint':
|
||||
'請求會從當前瀏覽器直連你設定的 Base URL,無中轉代理。Key 只存放在 localStorage。',
|
||||
'settings.skipForNow': '暫時跳過',
|
||||
|
||||
@@ -26,6 +26,7 @@ export async function streamMessageAnthropicProxy(
|
||||
model: cfg.model,
|
||||
systemPrompt: system,
|
||||
messages: history.map((m) => ({ role: m.role, content: m.content })),
|
||||
maxTokens: cfg.maxTokens,
|
||||
}),
|
||||
signal,
|
||||
});
|
||||
|
||||
@@ -57,7 +57,7 @@ export async function streamMessage(
|
||||
const stream = client.messages.stream(
|
||||
{
|
||||
model: cfg.model,
|
||||
max_tokens: 8192,
|
||||
max_tokens: cfg.maxTokens ?? 8192,
|
||||
system,
|
||||
messages: history.map((m) => ({ role: m.role, content: m.content })),
|
||||
},
|
||||
|
||||
@@ -33,6 +33,7 @@ export async function streamMessageOpenAI(
|
||||
model: cfg.model,
|
||||
systemPrompt: system,
|
||||
messages: history.map((m) => ({ role: m.role, content: m.content })),
|
||||
maxTokens: cfg.maxTokens,
|
||||
}),
|
||||
signal,
|
||||
});
|
||||
|
||||
@@ -58,6 +58,9 @@ export interface AppConfig {
|
||||
// Pre-existing configs without this field fall through to the agent's
|
||||
// declared default.
|
||||
agentModels?: Record<string, AgentModelChoice>;
|
||||
// Caps the upstream completion length in API mode. Defaults to 8192 when
|
||||
// unset; raise it for providers (e.g. MiMo) that allow longer responses.
|
||||
maxTokens?: number;
|
||||
}
|
||||
|
||||
export type AgentEvent = PersistedAgentEvent;
|
||||
|
||||
@@ -11,6 +11,9 @@ export interface ProxyStreamRequest {
|
||||
model: string;
|
||||
systemPrompt?: string;
|
||||
messages: ProxyMessage[];
|
||||
// Caps the upstream completion length. Defaults to 8192 when unset so
|
||||
// pre-existing clients keep their old behavior.
|
||||
maxTokens?: number;
|
||||
}
|
||||
|
||||
export interface ProxyStreamStartPayload {
|
||||
|
||||
Reference in New Issue
Block a user