diff --git a/package-lock.json b/package-lock.json index f3fe6b5..c4bb28a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codepilot", - "version": "0.19.2", + "version": "0.20.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codepilot", - "version": "0.19.2", + "version": "0.20.0", "dependencies": { "@ai-sdk/anthropic": "^3.0.47", "@ai-sdk/google": "^3.0.31", diff --git a/package.json b/package.json index 0d046fc..97a35af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codepilot", - "version": "0.19.2", + "version": "0.20.0", "private": true, "author": { "name": "op7418", diff --git a/src/app/api/providers/models/route.ts b/src/app/api/providers/models/route.ts index 03dc410..c77aced 100644 --- a/src/app/api/providers/models/route.ts +++ b/src/app/api/providers/models/route.ts @@ -51,6 +51,15 @@ const PROVIDER_MODEL_LABELS: Record { value: 'opus', label: 'Opus 4.6' }, { value: 'haiku', label: 'Haiku 4.5' }, ], + 'https://coding.dashscope.aliyuncs.com/apps/anthropic': [ + { value: 'qwen3.5-plus', label: 'Qwen 3.5 Plus' }, + { value: 'qwen3-coder-next', label: 'Qwen 3 Coder Next' }, + { value: 'qwen3-coder-plus', label: 'Qwen 3 Coder Plus' }, + { value: 'kimi-k2.5', label: 'Kimi K2.5' }, + { value: 'glm-5', label: 'GLM-5' }, + { value: 'glm-4.7', label: 'GLM-4.7' }, + { value: 'MiniMax-M2.5', label: 'MiniMax-M2.5' }, + ], }; /** @@ -90,7 +99,19 @@ export async function GET() { for (const provider of providers) { if (MEDIA_PROVIDER_TYPES.has(provider.provider_type)) continue; const matched = PROVIDER_MODEL_LABELS[provider.base_url]; - const rawModels = matched || DEFAULT_MODELS; + let rawModels = matched || DEFAULT_MODELS; + + // For providers with ANTHROPIC_MODEL in extra_env (e.g. Volcengine Ark), + // show the configured model name in the selector + if (!matched) { + try { + const envObj = JSON.parse(provider.extra_env || '{}'); + if (envObj.ANTHROPIC_MODEL) { + rawModels = [{ value: envObj.ANTHROPIC_MODEL, label: envObj.ANTHROPIC_MODEL }]; + } + } catch { /* use default */ } + } + const models = deduplicateModels(rawModels); groups.push({ diff --git a/src/components/chat/MessageInput.tsx b/src/components/chat/MessageInput.tsx index 5bb4f74..b303923 100644 --- a/src/components/chat/MessageInput.tsx +++ b/src/components/chat/MessageInput.tsx @@ -81,18 +81,6 @@ const IMAGE_AGENT_SYSTEM_PROMPT = `你是一个图像生成助手。当用户请 - 如果用户要求修改上一张生成的图片,必须加 "useLastGenerated": true - 在输出结构化块之前,可以先简要说明你的理解和计划`; -// Accepted file types for upload -const ACCEPTED_FILE_TYPES = [ - 'image/jpeg', 'image/png', 'image/gif', 'image/webp', - 'application/pdf', - 'text/*', - '.md', '.json', '.csv', '.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs', -].join(','); - -// Max file sizes -const MAX_IMAGE_SIZE = 5 * 1024 * 1024; // 5MB -const MAX_DOC_SIZE = 10 * 1024 * 1024; // 10MB -const MAX_FILE_SIZE = MAX_DOC_SIZE; // Use larger limit; we validate per-type in conversion interface MessageInputProps { onSend: (content: string, files?: FileAttachment[], systemPromptAppend?: string, displayOverride?: string) => void; @@ -631,12 +619,7 @@ export function MessageInput({ file.filename || 'file', file.mediaType || 'application/octet-stream', ); - // Enforce per-type size limits - const isImage = attachment.type.startsWith('image/'); - const sizeLimit = isImage ? MAX_IMAGE_SIZE : MAX_DOC_SIZE; - if (attachment.size <= sizeLimit) { - attachments.push(attachment); - } + attachments.push(attachment); } catch { // Skip files that fail conversion } @@ -1082,9 +1065,8 @@ export function MessageInput({ {/* PromptInput replaces the old input area */} {/* Bridge: listens for file tree "+" button events */} diff --git a/src/components/chat/StreamingMessage.tsx b/src/components/chat/StreamingMessage.tsx index 73f7b74..8f2965e 100644 --- a/src/components/chat/StreamingMessage.tsx +++ b/src/components/chat/StreamingMessage.tsx @@ -219,7 +219,7 @@ function AskUserQuestionUI({ @@ -439,17 +439,6 @@ export function StreamingMessage({ /> )} - {/* Permission approval — AskUserQuestion gets a dedicated UI */} - {pendingPermission?.toolName === 'AskUserQuestion' && !permissionResolved && ( - } - onSubmit={(decision, updatedInput) => onPermissionResponse?.(decision, updatedInput)} - /> - )} - {pendingPermission?.toolName === 'AskUserQuestion' && permissionResolved && ( -

Answer submitted

- )} - {/* Permission approval — ExitPlanMode gets a dedicated UI */} {pendingPermission?.toolName === 'ExitPlanMode' && !permissionResolved && ( {stripped} : null; })()} + {/* Permission approval — AskUserQuestion rendered after text so it stays in view */} + {pendingPermission?.toolName === 'AskUserQuestion' && !permissionResolved && ( + } + onSubmit={(decision, updatedInput) => onPermissionResponse?.(decision, updatedInput)} + /> + )} + {pendingPermission?.toolName === 'AskUserQuestion' && permissionResolved && ( +

Answer submitted

+ )} + {/* Loading indicator when no content yet */} {isStreaming && !content && toolUses.length === 0 && !pendingPermission && (
diff --git a/src/components/settings/ProviderManager.tsx b/src/components/settings/ProviderManager.tsx index ad55583..9e8a795 100644 --- a/src/components/settings/ProviderManager.tsx +++ b/src/components/settings/ProviderManager.tsx @@ -37,6 +37,7 @@ import { ProviderForm } from "./ProviderForm"; import type { ProviderFormData } from "./ProviderForm"; import type { ApiProvider } from "@/types"; import { useTranslation } from "@/hooks/useTranslation"; +import type { TranslationKey } from "@/i18n"; import Anthropic from "@lobehub/icons/es/Anthropic"; import OpenRouter from "@lobehub/icons/es/OpenRouter"; import Zhipu from "@lobehub/icons/es/Zhipu"; @@ -46,6 +47,8 @@ import Minimax from "@lobehub/icons/es/Minimax"; import Aws from "@lobehub/icons/es/Aws"; import Bedrock from "@lobehub/icons/es/Bedrock"; import Google from "@lobehub/icons/es/Google"; +import Volcengine from "@lobehub/icons/es/Volcengine"; +import Bailian from "@lobehub/icons/es/Bailian"; // --------------------------------------------------------------------------- // Brand icon resolver @@ -62,6 +65,10 @@ function getProviderIcon(name: string, baseUrl: string): ReactNode { if (url.includes("kimi.com") || lower.includes("kimi")) return ; if (url.includes("moonshot") || lower.includes("moonshot")) return ; if (url.includes("minimax") || lower.includes("minimax")) return ; + if (url.includes("volces.com") || url.includes("volcengine") || lower.includes("volcengine") || lower.includes("火山") || lower.includes("doubao") || lower.includes("豆包")) + return ; + if (url.includes("dashscope") || lower.includes("bailian") || lower.includes("百炼") || lower.includes("aliyun")) + return ; if (lower.includes("bedrock")) return ; if (lower.includes("vertex") || lower.includes("google")) return ; if (lower.includes("aws")) return ; @@ -118,7 +125,7 @@ const QUICK_PRESETS: QuickPreset[] = [ description: "Anthropic-compatible API — provide URL and Key", descriptionZh: "Anthropic 兼容第三方 API — 填写地址和密钥", icon: , - provider_type: "custom", + provider_type: "anthropic", base_url: "", extra_env: '{"ANTHROPIC_API_KEY":""}', fields: ["name", "api_key", "base_url", "model_names"], @@ -211,6 +218,28 @@ const QUICK_PRESETS: QuickPreset[] = [ extra_env: '{"API_TIMEOUT_MS":"3000000","CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC":"1","ANTHROPIC_API_KEY":""}', fields: ["api_key"], }, + { + key: "volcengine", + name: "Volcengine Ark", + description: "Volcengine Ark Coding Plan — Doubao, GLM, DeepSeek, Kimi", + descriptionZh: "字节火山方舟 Coding Plan — 豆包、GLM、DeepSeek、Kimi", + icon: , + provider_type: "custom", + base_url: "https://ark.cn-beijing.volces.com/api/coding", + extra_env: '{"ANTHROPIC_AUTH_TOKEN":""}', + fields: ["api_key", "model_names"], + }, + { + key: "bailian", + name: "Aliyun Bailian", + description: "Aliyun Bailian Coding Plan — Qwen, GLM, Kimi, MiniMax", + descriptionZh: "阿里云百炼 Coding Plan — 通义千问、GLM、Kimi、MiniMax", + icon: , + provider_type: "custom", + base_url: "https://coding.dashscope.aliyuncs.com/apps/anthropic", + extra_env: '{"ANTHROPIC_AUTH_TOKEN":""}', + fields: ["api_key"], + }, { key: "bedrock", name: "AWS Bedrock", @@ -294,6 +323,7 @@ function PresetConnectDialog({ const [baseUrl, setBaseUrl] = useState(""); const [name, setName] = useState(""); const [extraEnv, setExtraEnv] = useState("{}"); + const [modelName, setModelName] = useState(""); const [showAdvanced, setShowAdvanced] = useState(false); const [error, setError] = useState(null); const [saving, setSaving] = useState(false); @@ -307,6 +337,7 @@ function PresetConnectDialog({ setBaseUrl(preset.base_url); setName(preset.name); setExtraEnv(preset.extra_env); + setModelName(""); setError(null); setSaving(false); setShowAdvanced(false); @@ -329,6 +360,15 @@ function PresetConnectDialog({ } catch { /* use as-is */ } } + // Inject model name into extra_env if model_names field is used + if (preset.fields.includes("model_names") && modelName.trim()) { + try { + const envObj = JSON.parse(finalExtraEnv); + envObj["ANTHROPIC_MODEL"] = modelName.trim(); + finalExtraEnv = JSON.stringify(envObj); + } catch { /* use as-is */ } + } + // Validate extra_env JSON try { JSON.parse(finalExtraEnv); @@ -410,6 +450,24 @@ function PresetConnectDialog({
)} + {/* Model name — for providers that need user-specified model */} + {preset.fields.includes("model_names") && ( +
+ + setModelName(e.target.value)} + placeholder="ark-code-latest" + className="text-sm font-mono" + /> +

+ {isZh + ? '在服务商控制台配置的模型名称,如 ark-code-latest、doubao-seed-2.0-code' + : 'Model name configured in provider console, e.g. ark-code-latest'} +

+
+ )} + {/* Extra env — bedrock/vertex/custom always shown */} {preset.fields.includes("extra_env") && (
diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 02257fb..dddd916 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -128,6 +128,7 @@ const en = { 'provider.providerType': 'Provider Type', 'provider.apiKey': 'API Key', 'provider.baseUrl': 'Base URL', + 'provider.modelName': 'Model Name', 'provider.advancedOptions': 'Advanced Options', 'provider.extraEnvVars': 'Extra Environment Variables', 'provider.notes': 'Notes', diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index 3b32b28..59149b1 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -125,6 +125,7 @@ const zh: Record = { 'provider.providerType': '服务商类型', 'provider.apiKey': 'API 密钥', 'provider.baseUrl': '基础 URL', + 'provider.modelName': '模型名称', 'provider.advancedOptions': '高级选项', 'provider.extraEnvVars': '额外环境变量', 'provider.notes': '备注',