From d23dfecbcd756d1c30cfd75aab7e005e23083a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B2=E6=82=A6?= Date: Wed, 27 Aug 2025 16:52:04 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E9=85=8D=E7=BD=AE=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E4=BB=A5=E7=A7=BB=E9=99=A4ini=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=BC=BA`baseURL`=E6=94=AF=E6=8C=81=E5=B9=B6?= =?UTF-8?q?=E6=B1=89=E5=8C=96AI=E6=8F=90=E7=A4=BA=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/config.ts | 7 ++++++- src/utils/config.ts | 34 ++++++++++++++++++++-------------- src/utils/openai.ts | 26 +++++++++++++++++++++----- src/utils/prompt.ts | 37 ++++++++++++++++++++----------------- 4 files changed, 67 insertions(+), 37 deletions(-) diff --git a/src/commands/config.ts b/src/commands/config.ts index 408cf64..130e969 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -25,7 +25,12 @@ export default command( if (mode === 'set') { await setConfigs( - keyValues.map((keyValue) => keyValue.split('=') as [string, string]) + keyValues.map((keyValue) => { + const separatorIndex = keyValue.indexOf('='); + const key = keyValue.slice(0, separatorIndex); + const value = keyValue.slice(separatorIndex + 1); + return [key, value] as [string, string]; + }) ); return; } diff --git a/src/utils/config.ts b/src/utils/config.ts index e09dbb4..b326fdd 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,7 +1,6 @@ import fs from 'fs/promises'; import path from 'path'; import os from 'os'; -import ini from 'ini'; import type { TiktokenModel } from '@dqbd/tiktoken'; import { fileExists } from './fs.js'; import { KnownError } from './error.js'; @@ -117,18 +116,12 @@ const configParsers = { }, baseURL(url?: string) { if (!url || url.length === 0) { - return 'api.openai.com'; - } - - parseAssert('baseURL', /^https?:\/\/.+/.test(url), 'Must be a valid URL'); - - // 如果用户输入完整URL,提取域名部分 - try { - const urlObj = new URL(url); - return urlObj.hostname; - } catch { - return url; + return undefined; } + + parseAssert('baseURL', /^https?:\/\//.test(url), 'Must be a valid URL'); + + return url; }, } as const; @@ -151,7 +144,16 @@ const readConfigFile = async (): Promise => { } const configString = await fs.readFile(configPath, 'utf8'); - return ini.parse(configString); + const config = Object.create(null); + for (const line of configString.split(/\r?\n/)) { + const separatorIndex = line.indexOf('='); + if (separatorIndex !== -1) { + const key = line.slice(0, separatorIndex); + const value = line.slice(separatorIndex + 1); + config[key.trim()] = value.trim(); + } + } + return config; }; export const getConfig = async ( @@ -189,5 +191,9 @@ export const setConfigs = async (keyValues: [key: string, value: string][]) => { config[key as ConfigKeys] = parsed as any; } - await fs.writeFile(configPath, ini.stringify(config), 'utf8'); + const configString = Object.entries(config) + .filter(([, value]) => value !== undefined && value !== null) + .map(([key, value]) => `${key}=${value}`) + .join(os.EOL); + await fs.writeFile(configPath, configString, 'utf8'); }; diff --git a/src/utils/openai.ts b/src/utils/openai.ts index 12711c6..30923a6 100644 --- a/src/utils/openai.ts +++ b/src/utils/openai.ts @@ -39,7 +39,7 @@ const httpsPost = async ( 'Content-Length': Buffer.byteLength(postContent), }, timeout, - agent: proxy ? createHttpsProxyAgent(proxy) : undefined, + agent: (proxy && proxy.trim() !== '') ? createHttpsProxyAgent(proxy) : undefined as any, }, (response) => { const body: Buffer[] = []; @@ -74,10 +74,14 @@ const createChatCompletion = async ( proxy?: string, baseURL?: string ) => { - const hostname = baseURL || 'api.openai.com'; + const url = baseURL ? new URL(baseURL) : new URL('https://api.openai.com/v1/chat/completions'); + if (url.pathname === '/') { + url.pathname = '/v1/chat/completions'; + } + const { response, data } = await httpsPost( - hostname, - '/v1/chat/completions', + url.hostname, + url.pathname, { Authorization: `Bearer ${apiKey}`, }, @@ -104,7 +108,19 @@ const createChatCompletion = async ( throw new KnownError(errorMessage); } - return JSON.parse(data) as CreateChatCompletionResponse; + try { + const json = JSON.parse(data); + + if (json.error) { + throw new KnownError(json.error.message); + } + + return json as CreateChatCompletionResponse; + } catch (error) { + throw new KnownError( + `Error parsing response: ${error}\n\n${data}` + ); + } }; const sanitizeMessage = (message: string) => diff --git a/src/utils/prompt.ts b/src/utils/prompt.ts index 44fb4bb..a47bed8 100644 --- a/src/utils/prompt.ts +++ b/src/utils/prompt.ts @@ -5,7 +5,7 @@ const commitTypeFormats: Record = { conventional: '(): ', }; const specifyCommitFormat = (type: CommitType) => - `The output response must be in format:\n${commitTypeFormats[type]}`; + `输出响应必须使用以下格式:\n${commitTypeFormats[type]}`; const commitTypes: Record = { '': '', @@ -18,20 +18,20 @@ const commitTypes: Record = { * Conventional Changelog: * https://github.com/conventional-changelog/conventional-changelog/blob/d0e5d5926c8addba74bc962553dd8bcfba90e228/packages/conventional-changelog-conventionalcommits/writer-opts.js#L182-L193 */ - conventional: `Choose a type from the type-to-description JSON below that best describes the git diff:\n${JSON.stringify( + conventional: `从下面的类型到描述的JSON中选择最能描述git差异的类型:\n${JSON.stringify( { - docs: 'Documentation only changes', + docs: '仅文档更改', style: - 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)', - refactor: 'A code change that neither fixes a bug nor adds a feature', - perf: 'A code change that improves performance', - test: 'Adding missing tests or correcting existing tests', - build: 'Changes that affect the build system or external dependencies', - ci: 'Changes to our CI configuration files and scripts', - chore: "Other changes that don't modify src or test files", - revert: 'Reverts a previous commit', - feat: 'A new feature', - fix: 'A bug fix', + '不影响代码含义的更改(空格、格式、缺少分号等)', + refactor: '既不修复错误也不添加功能的代码更改', + perf: '提高性能的代码更改', + test: '添加缺失的测试或更正现有测试', + build: '影响构建系统或外部依赖的更改', + ci: '对我们的CI配置文件和脚本的更改', + chore: '不修改src或测试文件的其他更改', + revert: '恢复之前的提交', + feat: '新功能', + fix: '错误修复', }, null, 2 @@ -44,10 +44,13 @@ export const generatePrompt = ( type: CommitType ) => [ - 'Generate a concise git commit message written in present tense for the following code diff with the given specifications below:', - `Message language: ${locale}`, - `Commit message must be a maximum of ${maxLength} characters.`, - 'Exclude anything unnecessary such as translation. Your entire response will be passed directly into git commit.', + '为以下代码差异生成一个简洁的、使用现在时态的中文git提交消息,并遵循以下规范:', + `提交消息使用中文编写。`, + `提交消息最多${maxLength}个字符。`, + '提交消息包含具体的类名、方法名或其他关键信息,不能过于笼统。', + '对于功能添加,应该指明具体的类或模块,如"在UserController中添加了用户权限验证功能"。', + '对于代码重构,应该指明重构的具体类或方法,如"重构了PaymentProcessor类的金额计算逻辑"。', + '排除任何不必要的内容,如翻译。您的整个响应将直接传递到git提交中。', commitTypes[type], specifyCommitFormat(type), ]