fix: separate Anthropic output and context limits
Direct Anthropic connections reused the configured context window as max_tokens, which could impose an unintended output value or send a provider-invalid request. Anthropic connections now expose an independent per-response maximum output setting, defaulting to 32K for the protocol-required field, while OpenAI direct requests remain without an added output token cap and direct tool rounds remain unmetered. Release note: Anthropic 直连模型现在可单独配置单次最大输出,不再复用上下文窗口;OpenAI 直连请求和工具调用轮次不增加额外配额。
This commit is contained in:
@@ -20,7 +20,7 @@ GoodBuddy是一个开箱即用的 AI 生产平台,安全、跨平台、本地
|
|||||||
- **安全执行**:`Ask` 保持只读;`Execute` 仅运行已启用且受边界约束的工具,并保留活动记录。
|
- **安全执行**:`Ask` 保持只读;`Execute` 仅运行已启用且受边界约束的工具,并保留活动记录。
|
||||||
- **本地优先**:会话、任务、成果、记忆、知识库和图谱保存在本地 SQLite;API Key 由系统安全存储加密。
|
- **本地优先**:会话、任务、成果、记忆、知识库和图谱保存在本地 SQLite;API Key 由系统安全存储加密。
|
||||||
- **多 Runtime**:支持直连模型、OpenCode、Continue 和预览版 DeepSeek Harness,统一处理取消、超时、输出限制和进程退出;原生能力清单将 Tools 与 Commands、LSP、Formatters 分开,并显示来源及 Ask/Execute 可用性。内置 OpenCode 提供 Agent、Tool、Command 与原生 Compact,Continue 提供 Rules、Prompt 预设、结构化提问与 GoodBuddy 手动摘要压缩,并明确标记当前版本无法静态发现原生 Tools。
|
- **多 Runtime**:支持直连模型、OpenCode、Continue 和预览版 DeepSeek Harness,统一处理取消、超时、输出限制和进程退出;原生能力清单将 Tools 与 Commands、LSP、Formatters 分开,并显示来源及 Ask/Execute 可用性。内置 OpenCode 提供 Agent、Tool、Command 与原生 Compact,Continue 提供 Rules、Prompt 预设、结构化提问与 GoodBuddy 手动摘要压缩,并明确标记当前版本无法静态发现原生 Tools。
|
||||||
- **开放连接**:支持 OpenAI Responses、OpenAI 兼容 Chat Completions、Anthropic Messages、OpenAI Images、Embeddings、跨 Runtime Skills 与自定义 MCP,以及默认关闭、由用户显式开启的 DeepSeek Harness npm 插件市场。MCP 测试可读取有界 Prompt/Resource 元数据,但不会读取 Resource 内容。
|
- **开放连接**:支持 OpenAI Responses、OpenAI 兼容 Chat Completions、Anthropic Messages、OpenAI Images、Embeddings、跨 Runtime Skills 与自定义 MCP,以及默认关闭、由用户显式开启的 DeepSeek Harness npm 插件市场。Anthropic 连接可独立配置协议必填的单次最大输出,不再复用上下文窗口;OpenAI 直连请求不额外设置输出 Token 上限。MCP 测试可读取有界 Prompt/Resource 元数据,但不会读取 Resource 内容。
|
||||||
- **知识工作区**:支持文件、目录和网页导入,以及全文、词组、向量和图谱混合检索。
|
- **知识工作区**:支持文件、目录和网页导入,以及全文、词组、向量和图谱混合检索。
|
||||||
- **工作管理**:集中管理 Projects、对话、任务、活动、成果、记忆、魔法笔记和智能心跳;可从稳定的本地会话复制当前聊天内容并创建互不影响的会话分支。
|
- **工作管理**:集中管理 Projects、对话、任务、活动、成果、记忆、魔法笔记和智能心跳;可从稳定的本地会话复制当前聊天内容并创建互不影响的会话分支。
|
||||||
- **远程通道**:支持微信 ClawBot、企业微信和钉钉,每个发送者使用独立远程会话。
|
- **远程通道**:支持微信 ClawBot、企业微信和钉钉,每个发送者使用独立远程会话。
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ Linux x64 和 Linux arm64。
|
|||||||
导航及重定向仍经过 DNS 解析和目标地址固定。
|
导航及重定向仍经过 DNS 解析和目标地址固定。
|
||||||
- 已实现回环过滤代理、下载和文件选择器阻止、权限拒绝、会话取消、
|
- 已实现回环过滤代理、下载和文件选择器阻止、权限拒绝、会话取消、
|
||||||
空闲回收和应用退出清理。
|
空闲回收和应用退出清理。
|
||||||
- 直连模型工具循环已提高到适合浏览器任务的有界上限,并包含重复调用和
|
- 直连模型工具循环不设置工具次数或模型轮次配额;取消、请求超时、工具结果大小和
|
||||||
无进展保护。
|
累计输出边界继续防止失控执行。
|
||||||
|
|
||||||
### MCP
|
### MCP
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ function resolveContextCompression(
|
|||||||
'openai-images-generations'
|
'openai-images-generations'
|
||||||
>,
|
>,
|
||||||
authentication: summaryProfile.authentication,
|
authentication: summaryProfile.authentication,
|
||||||
contextWindowTokens: summaryProfile.contextWindowTokens
|
contextWindowTokens: summaryProfile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: summaryProfile.maximumOutputTokens
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
@@ -120,6 +121,7 @@ export function createDefaultModelRuntime(
|
|||||||
protocol: settings.modelProtocol,
|
protocol: settings.modelProtocol,
|
||||||
authentication: settings.modelAuthentication,
|
authentication: settings.modelAuthentication,
|
||||||
supportsImageInput: settings.supportsImageInput,
|
supportsImageInput: settings.supportsImageInput,
|
||||||
|
maximumOutputTokens: currentProfile?.maximumOutputTokens,
|
||||||
defaultWorkspace: settings.workspacePath || defaultWorkspace,
|
defaultWorkspace: settings.workspacePath || defaultWorkspace,
|
||||||
contextCompression: resolveContextCompression(
|
contextCompression: resolveContextCompression(
|
||||||
settings,
|
settings,
|
||||||
@@ -141,6 +143,7 @@ export function createModelProfileRuntime(
|
|||||||
protocol: profile.protocol,
|
protocol: profile.protocol,
|
||||||
authentication: profile.authentication,
|
authentication: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput,
|
supportsImageInput: profile.supportsImageInput,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality:
|
imageGenerationQuality:
|
||||||
profile.imageGenerationQuality ??
|
profile.imageGenerationQuality ??
|
||||||
defaultRuntimeSettings.imageGenerationQuality,
|
defaultRuntimeSettings.imageGenerationQuality,
|
||||||
|
|||||||
@@ -476,6 +476,7 @@ describe('ModelAgentRuntime', () => {
|
|||||||
model: 'sonnet-5',
|
model: 'sonnet-5',
|
||||||
protocol: 'anthropic-messages',
|
protocol: 'anthropic-messages',
|
||||||
authentication: 'api-key',
|
authentication: 'api-key',
|
||||||
|
maximumOutputTokens: 48_000,
|
||||||
skillInstructions: '# 文档写作',
|
skillInstructions: '# 文档写作',
|
||||||
fetcher
|
fetcher
|
||||||
})
|
})
|
||||||
@@ -500,11 +501,13 @@ describe('ModelAgentRuntime', () => {
|
|||||||
|
|
||||||
const body = JSON.parse(init?.body as string) as {
|
const body = JSON.parse(init?.body as string) as {
|
||||||
model: string
|
model: string
|
||||||
|
max_tokens: number
|
||||||
stream: boolean
|
stream: boolean
|
||||||
system: string
|
system: string
|
||||||
}
|
}
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
model: 'sonnet-5',
|
model: 'sonnet-5',
|
||||||
|
max_tokens: 48_000,
|
||||||
stream: true
|
stream: true
|
||||||
})
|
})
|
||||||
expect(body.system).toMatch(
|
expect(body.system).toMatch(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type {
|
|||||||
ModelProtocol,
|
ModelProtocol,
|
||||||
RuntimeConversationCompactInput
|
RuntimeConversationCompactInput
|
||||||
} from '../../shared/contracts'
|
} from '../../shared/contracts'
|
||||||
|
import { defaultAnthropicMaximumOutputTokens } from '../../shared/contracts'
|
||||||
import type { ResolvedMcpServer } from '../capabilities/capability-service'
|
import type { ResolvedMcpServer } from '../capabilities/capability-service'
|
||||||
import type { BrowserToolService } from '../browser/browser-model-tools'
|
import type { BrowserToolService } from '../browser/browser-model-tools'
|
||||||
import type { WorkspaceAccess } from '../workspace'
|
import type { WorkspaceAccess } from '../workspace'
|
||||||
@@ -233,8 +234,10 @@ export type ModelRuntimeOptions = {
|
|||||||
protocol: Exclude<ModelProtocol, 'openai-images-generations'>
|
protocol: Exclude<ModelProtocol, 'openai-images-generations'>
|
||||||
authentication: ModelAuthentication
|
authentication: ModelAuthentication
|
||||||
contextWindowTokens?: number
|
contextWindowTokens?: number
|
||||||
|
maximumOutputTokens?: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
maximumOutputTokens?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function getErrorMessage(value: unknown): string | undefined {
|
function getErrorMessage(value: unknown): string | undefined {
|
||||||
@@ -1794,10 +1797,9 @@ export class ModelAgentRuntime implements AgentRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get anthropicProtocolMaximumTokens(): number {
|
private get anthropicProtocolMaximumTokens(): number {
|
||||||
return Math.max(
|
return (
|
||||||
this.options.contextCompression?.contextWindowTokens ??
|
this.options.maximumOutputTokens ??
|
||||||
minimumModelContextWindowTokens,
|
defaultAnthropicMaximumOutputTokens
|
||||||
minimumModelContextWindowTokens
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1877,7 +1879,8 @@ export class ModelAgentRuntime implements AgentRuntime {
|
|||||||
ModelProtocol,
|
ModelProtocol,
|
||||||
'openai-images-generations'
|
'openai-images-generations'
|
||||||
>,
|
>,
|
||||||
authentication: this.options.authentication
|
authentication: this.options.authentication,
|
||||||
|
maximumOutputTokens: this.options.maximumOutputTokens
|
||||||
}
|
}
|
||||||
const summaryRuntime = new ModelAgentRuntime({
|
const summaryRuntime = new ModelAgentRuntime({
|
||||||
...summaryModel,
|
...summaryModel,
|
||||||
|
|||||||
@@ -386,6 +386,7 @@ export function createResolvedModelProfileDigest(
|
|||||||
authentication: profile.authentication,
|
authentication: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput === true,
|
supportsImageInput: profile.supportsImageInput === true,
|
||||||
contextWindowTokens: profile.contextWindowTokens ?? null,
|
contextWindowTokens: profile.contextWindowTokens ?? null,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens ?? null,
|
||||||
imageGenerationQuality: profile.imageGenerationQuality ?? null
|
imageGenerationQuality: profile.imageGenerationQuality ?? null
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -283,6 +283,7 @@ describe('RuntimeSettingsStore', () => {
|
|||||||
authentication: 'none',
|
authentication: 'none',
|
||||||
supportsImageInput: false,
|
supportsImageInput: false,
|
||||||
contextWindowTokens: 256_000,
|
contextWindowTokens: 256_000,
|
||||||
|
maximumOutputTokens: 48_000,
|
||||||
imageGenerationQuality: 'auto',
|
imageGenerationQuality: 'auto',
|
||||||
apiKey: { action: 'keep' }
|
apiKey: { action: 'keep' }
|
||||||
}
|
}
|
||||||
@@ -302,7 +303,8 @@ describe('RuntimeSettingsStore', () => {
|
|||||||
modelProfiles: [
|
modelProfiles: [
|
||||||
{
|
{
|
||||||
id: profileId,
|
id: profileId,
|
||||||
contextWindowTokens: 256_000
|
contextWindowTokens: 256_000,
|
||||||
|
maximumOutputTokens: 48_000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
contextCompression: {
|
contextCompression: {
|
||||||
@@ -316,7 +318,8 @@ describe('RuntimeSettingsStore', () => {
|
|||||||
modelProfiles: [
|
modelProfiles: [
|
||||||
{
|
{
|
||||||
id: profileId,
|
id: profileId,
|
||||||
contextWindowTokens: 256_000
|
contextWindowTokens: 256_000,
|
||||||
|
maximumOutputTokens: 48_000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
contextCompression: {
|
contextCompression: {
|
||||||
|
|||||||
@@ -170,6 +170,12 @@ const currentStoredModelProfileSchema = storedModelProfileSchema.extend({
|
|||||||
.int()
|
.int()
|
||||||
.min(8_000)
|
.min(8_000)
|
||||||
.max(10_000_000)
|
.max(10_000_000)
|
||||||
|
.optional(),
|
||||||
|
maximumOutputTokens: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.max(10_000_000)
|
||||||
.optional()
|
.optional()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -434,6 +440,7 @@ export type ResolvedModelProfile = {
|
|||||||
authentication: RuntimeSettings['modelAuthentication']
|
authentication: RuntimeSettings['modelAuthentication']
|
||||||
supportsImageInput?: boolean
|
supportsImageInput?: boolean
|
||||||
contextWindowTokens?: number
|
contextWindowTokens?: number
|
||||||
|
maximumOutputTokens?: number
|
||||||
imageGenerationQuality?: RuntimeSettings['imageGenerationQuality']
|
imageGenerationQuality?: RuntimeSettings['imageGenerationQuality']
|
||||||
apiKey?: string
|
apiKey?: string
|
||||||
}
|
}
|
||||||
@@ -1468,6 +1475,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: RuntimeSettings['modelAuthentication']
|
authentication: RuntimeSettings['modelAuthentication']
|
||||||
supportsImageInput: boolean
|
supportsImageInput: boolean
|
||||||
contextWindowTokens?: number
|
contextWindowTokens?: number
|
||||||
|
maximumOutputTokens?: number
|
||||||
imageGenerationQuality: RuntimeSettings['imageGenerationQuality']
|
imageGenerationQuality: RuntimeSettings['imageGenerationQuality']
|
||||||
credentialSource: RuntimeSettings['credentialSource']
|
credentialSource: RuntimeSettings['credentialSource']
|
||||||
} {
|
} {
|
||||||
@@ -1529,6 +1537,7 @@ export class RuntimeSettingsStore {
|
|||||||
: profile.authentication,
|
: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput,
|
supportsImageInput: profile.supportsImageInput,
|
||||||
contextWindowTokens: profile.contextWindowTokens,
|
contextWindowTokens: profile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality: profile.imageGenerationQuality,
|
imageGenerationQuality: profile.imageGenerationQuality,
|
||||||
credentialSource: credential.source
|
credentialSource: credential.source
|
||||||
}
|
}
|
||||||
@@ -1552,6 +1561,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: effective.authentication,
|
authentication: effective.authentication,
|
||||||
supportsImageInput: effective.supportsImageInput,
|
supportsImageInput: effective.supportsImageInput,
|
||||||
contextWindowTokens: effective.contextWindowTokens,
|
contextWindowTokens: effective.contextWindowTokens,
|
||||||
|
maximumOutputTokens: effective.maximumOutputTokens,
|
||||||
imageGenerationQuality: effective.imageGenerationQuality,
|
imageGenerationQuality: effective.imageGenerationQuality,
|
||||||
apiKey: effective.apiKey
|
apiKey: effective.apiKey
|
||||||
}
|
}
|
||||||
@@ -1569,6 +1579,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: profile.authentication,
|
authentication: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput,
|
supportsImageInput: profile.supportsImageInput,
|
||||||
contextWindowTokens: profile.contextWindowTokens,
|
contextWindowTokens: profile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality: profile.imageGenerationQuality,
|
imageGenerationQuality: profile.imageGenerationQuality,
|
||||||
apiKey: credential.activeApiKey
|
apiKey: credential.activeApiKey
|
||||||
}
|
}
|
||||||
@@ -1651,6 +1662,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: resolved.authentication,
|
authentication: resolved.authentication,
|
||||||
supportsImageInput: resolved.supportsImageInput,
|
supportsImageInput: resolved.supportsImageInput,
|
||||||
contextWindowTokens: resolved.contextWindowTokens,
|
contextWindowTokens: resolved.contextWindowTokens,
|
||||||
|
maximumOutputTokens: resolved.maximumOutputTokens,
|
||||||
imageGenerationQuality:
|
imageGenerationQuality:
|
||||||
resolved.imageGenerationQuality ??
|
resolved.imageGenerationQuality ??
|
||||||
defaultRuntimeSettings.imageGenerationQuality,
|
defaultRuntimeSettings.imageGenerationQuality,
|
||||||
@@ -1672,6 +1684,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: profile.authentication,
|
authentication: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput,
|
supportsImageInput: profile.supportsImageInput,
|
||||||
contextWindowTokens: profile.contextWindowTokens,
|
contextWindowTokens: profile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality:
|
imageGenerationQuality:
|
||||||
profile.imageGenerationQuality ??
|
profile.imageGenerationQuality ??
|
||||||
defaultRuntimeSettings.imageGenerationQuality,
|
defaultRuntimeSettings.imageGenerationQuality,
|
||||||
@@ -1966,6 +1979,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: input.modelAuthentication,
|
authentication: input.modelAuthentication,
|
||||||
supportsImageInput: profile.supportsImageInput,
|
supportsImageInput: profile.supportsImageInput,
|
||||||
contextWindowTokens: profile.contextWindowTokens,
|
contextWindowTokens: profile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality: input.imageGenerationQuality,
|
imageGenerationQuality: input.imageGenerationQuality,
|
||||||
apiKey: input.apiKey
|
apiKey: input.apiKey
|
||||||
}
|
}
|
||||||
@@ -1978,6 +1992,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: profile.authentication,
|
authentication: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput,
|
supportsImageInput: profile.supportsImageInput,
|
||||||
contextWindowTokens: profile.contextWindowTokens,
|
contextWindowTokens: profile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality: profile.imageGenerationQuality,
|
imageGenerationQuality: profile.imageGenerationQuality,
|
||||||
apiKey: { action: 'keep' as const }
|
apiKey: { action: 'keep' as const }
|
||||||
}
|
}
|
||||||
@@ -2029,6 +2044,7 @@ export class RuntimeSettingsStore {
|
|||||||
authentication: profile.authentication,
|
authentication: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput ?? false,
|
supportsImageInput: profile.supportsImageInput ?? false,
|
||||||
contextWindowTokens: profile.contextWindowTokens,
|
contextWindowTokens: profile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality: profile.imageGenerationQuality
|
imageGenerationQuality: profile.imageGenerationQuality
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1112,6 +1112,10 @@ describe('SettingsPanel runtime files', () => {
|
|||||||
expect(contextWindow).toHaveValue(null)
|
expect(contextWindow).toHaveValue(null)
|
||||||
expect(contextWindow).toHaveAttribute('min', '32')
|
expect(contextWindow).toHaveAttribute('min', '32')
|
||||||
fireEvent.change(contextWindow, { target: { value: '256' } })
|
fireEvent.change(contextWindow, { target: { value: '256' } })
|
||||||
|
const maximumOutput = screen.getByLabelText('Anthropic 最大输出')
|
||||||
|
expect(maximumOutput).toHaveValue(32)
|
||||||
|
expect(maximumOutput).toHaveAttribute('min', '1')
|
||||||
|
fireEvent.change(maximumOutput, { target: { value: '48' } })
|
||||||
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
@@ -1120,7 +1124,8 @@ describe('SettingsPanel runtime files', () => {
|
|||||||
modelProfiles: expect.arrayContaining([
|
modelProfiles: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: modelProfileId,
|
id: modelProfileId,
|
||||||
contextWindowTokens: 256_000
|
contextWindowTokens: 256_000,
|
||||||
|
maximumOutputTokens: 48_000
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import type {
|
|||||||
} from '../../shared/contracts'
|
} from '../../shared/contracts'
|
||||||
import {
|
import {
|
||||||
defaultContextCompressionSettings,
|
defaultContextCompressionSettings,
|
||||||
|
defaultAnthropicMaximumOutputTokens,
|
||||||
builtinEmbeddingConnectionId,
|
builtinEmbeddingConnectionId,
|
||||||
defaultModelProfileId as builtInDefaultModelProfileId,
|
defaultModelProfileId as builtInDefaultModelProfileId,
|
||||||
defaultRuntimeSettings,
|
defaultRuntimeSettings,
|
||||||
@@ -257,6 +258,9 @@ function toModelProfileDrafts(
|
|||||||
return settings.modelProfiles.map((profile) => ({
|
return settings.modelProfiles.map((profile) => ({
|
||||||
...profile,
|
...profile,
|
||||||
supportsImageInput: profile.supportsImageInput ?? false,
|
supportsImageInput: profile.supportsImageInput ?? false,
|
||||||
|
maximumOutputTokens:
|
||||||
|
profile.maximumOutputTokens ??
|
||||||
|
defaultAnthropicMaximumOutputTokens,
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
clearApiKey: false
|
clearApiKey: false
|
||||||
}))
|
}))
|
||||||
@@ -1285,6 +1289,7 @@ export function SettingsPanel({
|
|||||||
authentication: profile.authentication,
|
authentication: profile.authentication,
|
||||||
supportsImageInput: profile.supportsImageInput,
|
supportsImageInput: profile.supportsImageInput,
|
||||||
contextWindowTokens: profile.contextWindowTokens,
|
contextWindowTokens: profile.contextWindowTokens,
|
||||||
|
maximumOutputTokens: profile.maximumOutputTokens,
|
||||||
imageGenerationQuality: profile.imageGenerationQuality,
|
imageGenerationQuality: profile.imageGenerationQuality,
|
||||||
apiKey: profile.clearApiKey
|
apiKey: profile.clearApiKey
|
||||||
? ({ action: 'clear' } as const)
|
? ({ action: 'clear' } as const)
|
||||||
@@ -1670,6 +1675,7 @@ export function SettingsPanel({
|
|||||||
protocol: 'openai-chat-completions',
|
protocol: 'openai-chat-completions',
|
||||||
authentication: defaultRuntimeSettings.modelAuthentication,
|
authentication: defaultRuntimeSettings.modelAuthentication,
|
||||||
supportsImageInput: defaultRuntimeSettings.supportsImageInput,
|
supportsImageInput: defaultRuntimeSettings.supportsImageInput,
|
||||||
|
maximumOutputTokens: defaultAnthropicMaximumOutputTokens,
|
||||||
imageGenerationQuality:
|
imageGenerationQuality:
|
||||||
defaultRuntimeSettings.imageGenerationQuality,
|
defaultRuntimeSettings.imageGenerationQuality,
|
||||||
apiKeyConfigured: false,
|
apiKeyConfigured: false,
|
||||||
@@ -3136,6 +3142,34 @@ export function SettingsPanel({
|
|||||||
{t('model.profile.contextWindowDescription')}
|
{t('model.profile.contextWindowDescription')}
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
|
{profile.protocol === 'anthropic-messages' && (
|
||||||
|
<label className="field">
|
||||||
|
<span>{t('model.profile.maximumOutput')}</span>
|
||||||
|
<input
|
||||||
|
aria-label={t('model.profile.maximumOutput')}
|
||||||
|
inputMode="numeric"
|
||||||
|
max={10_000}
|
||||||
|
min={1}
|
||||||
|
onChange={(event) => {
|
||||||
|
const value = event.target.valueAsNumber
|
||||||
|
updateModelProfile(profile.id, {
|
||||||
|
maximumOutputTokens: Number.isFinite(value)
|
||||||
|
? Math.round(value * 1_000)
|
||||||
|
: defaultAnthropicMaximumOutputTokens
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
placeholder="32"
|
||||||
|
type="number"
|
||||||
|
value={
|
||||||
|
(profile.maximumOutputTokens ??
|
||||||
|
defaultAnthropicMaximumOutputTokens) / 1_000
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<small>
|
||||||
|
{t('model.profile.maximumOutputDescription')}
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{profile.protocol ===
|
{profile.protocol ===
|
||||||
|
|||||||
@@ -721,6 +721,9 @@ export const settings = {
|
|||||||
contextWindow: 'Context window (optional)',
|
contextWindow: 'Context window (optional)',
|
||||||
contextWindowDescription:
|
contextWindowDescription:
|
||||||
'Enter 32K–10000K tokens. Leave blank when unknown. This value is used only for GoodBuddy local budget calculations.',
|
'Enter 32K–10000K tokens. Leave blank when unknown. This value is used only for GoodBuddy local budget calculations.',
|
||||||
|
maximumOutput: 'Anthropic maximum output',
|
||||||
|
maximumOutputDescription:
|
||||||
|
'The Anthropic Messages protocol requires this value on every request. Enter 1K–10000K tokens. It limits only one model response, not tool rounds or cumulative usage.',
|
||||||
imageQuality: 'Image quality',
|
imageQuality: 'Image quality',
|
||||||
imageQualityAriaLabel: 'Image quality for {{name}}',
|
imageQualityAriaLabel: 'Image quality for {{name}}',
|
||||||
quality: {
|
quality: {
|
||||||
|
|||||||
@@ -662,6 +662,9 @@ export const settings = {
|
|||||||
contextWindow: '上下文上限(可选)',
|
contextWindow: '上下文上限(可选)',
|
||||||
contextWindowDescription:
|
contextWindowDescription:
|
||||||
'以 K tokens 填写,范围为 32K–10000K。留空表示未知;此值仅用于 GoodBuddy 本地预算计算。',
|
'以 K tokens 填写,范围为 32K–10000K。留空表示未知;此值仅用于 GoodBuddy 本地预算计算。',
|
||||||
|
maximumOutput: 'Anthropic 最大输出',
|
||||||
|
maximumOutputDescription:
|
||||||
|
'Anthropic Messages 协议要求每次请求提供该值。填写 1K–10000K tokens;它只限制单次模型响应,不限制工具调用轮次或累计用量。',
|
||||||
imageQuality: '图片质量',
|
imageQuality: '图片质量',
|
||||||
imageQualityAriaLabel: '图片质量 {{name}}',
|
imageQualityAriaLabel: '图片质量 {{name}}',
|
||||||
quality: {
|
quality: {
|
||||||
|
|||||||
@@ -568,6 +568,9 @@ export {
|
|||||||
minimumModelContextWindowTokens
|
minimumModelContextWindowTokens
|
||||||
} from './context-window'
|
} from './context-window'
|
||||||
|
|
||||||
|
export const defaultAnthropicMaximumOutputTokens = 32_000
|
||||||
|
export const maximumModelOutputTokens = 10_000_000
|
||||||
|
|
||||||
const modelProfileInputSchema = z
|
const modelProfileInputSchema = z
|
||||||
.object({
|
.object({
|
||||||
id: modelProfileIdSchema,
|
id: modelProfileIdSchema,
|
||||||
@@ -588,6 +591,12 @@ const modelProfileInputSchema = z
|
|||||||
.min(minimumModelContextWindowTokens)
|
.min(minimumModelContextWindowTokens)
|
||||||
.max(maximumModelContextWindowTokens)
|
.max(maximumModelContextWindowTokens)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
maximumOutputTokens: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.max(maximumModelOutputTokens)
|
||||||
|
.optional(),
|
||||||
imageGenerationQuality: imageGenerationQualitySchema,
|
imageGenerationQuality: imageGenerationQualitySchema,
|
||||||
apiKey: modelApiKeyUpdateSchema
|
apiKey: modelApiKeyUpdateSchema
|
||||||
})
|
})
|
||||||
@@ -968,6 +977,7 @@ export type ModelConnectionSettings = {
|
|||||||
authentication: ModelAuthentication
|
authentication: ModelAuthentication
|
||||||
supportsImageInput?: boolean
|
supportsImageInput?: boolean
|
||||||
contextWindowTokens?: number
|
contextWindowTokens?: number
|
||||||
|
maximumOutputTokens?: number
|
||||||
imageGenerationQuality: ImageGenerationQuality
|
imageGenerationQuality: ImageGenerationQuality
|
||||||
apiKeyConfigured: boolean
|
apiKeyConfigured: boolean
|
||||||
credentialSource: 'none' | 'encrypted' | 'environment' | 'unreadable'
|
credentialSource: 'none' | 'encrypted' | 'environment' | 'unreadable'
|
||||||
|
|||||||
Reference in New Issue
Block a user