mirror of
https://github.com/di-sukharev/opencommit.git
synced 2026-04-20 03:02:51 -04:00
feat(proxy): add universal proxy support and fix Gemini model resolution (#536)
Integrated undici ProxyAgent for native fetch and HttpsProxyAgent for axios/openai/anthropic. Upgraded @google/generative-ai to fix #536. Added OCO_PROXY config. Co-authored-by: uni <uni@hanwei.ink>
This commit is contained in:
@@ -5,9 +5,10 @@ import { cli } from 'cleye';
|
||||
import packageJSON from '../package.json';
|
||||
import { commit } from './commands/commit';
|
||||
import { commitlintConfigCommand } from './commands/commitlint';
|
||||
import { configCommand } from './commands/config';
|
||||
import { configCommand, getConfig } from './commands/config';
|
||||
import { hookCommand, isHookCalled } from './commands/githook.js';
|
||||
import { prepareCommitMessageHook } from './commands/prepare-commit-msg-hook';
|
||||
import { setupProxy } from './utils/proxy';
|
||||
import {
|
||||
setupCommand,
|
||||
isFirstRun,
|
||||
@@ -18,6 +19,9 @@ import { modelsCommand } from './commands/models';
|
||||
import { checkIsLatestVersion } from './utils/checkIsLatestVersion';
|
||||
import { runMigrations } from './migrations/_run.js';
|
||||
|
||||
const config = getConfig();
|
||||
setupProxy(config.OCO_PROXY);
|
||||
|
||||
const extraArgs = process.argv.slice(2);
|
||||
|
||||
cli(
|
||||
|
||||
@@ -25,6 +25,7 @@ export enum CONFIG_KEYS {
|
||||
OCO_ONE_LINE_COMMIT = 'OCO_ONE_LINE_COMMIT',
|
||||
OCO_TEST_MOCK_TYPE = 'OCO_TEST_MOCK_TYPE',
|
||||
OCO_API_URL = 'OCO_API_URL',
|
||||
OCO_PROXY = 'OCO_PROXY',
|
||||
OCO_API_CUSTOM_HEADERS = 'OCO_API_CUSTOM_HEADERS',
|
||||
OCO_OMIT_SCOPE = 'OCO_OMIT_SCOPE',
|
||||
OCO_GITPUSH = 'OCO_GITPUSH', // todo: deprecate
|
||||
@@ -727,6 +728,15 @@ export const configValidators = {
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OCO_PROXY](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OCO_PROXY,
|
||||
typeof value === 'string',
|
||||
`${value} is not a valid URL. It should start with 'http://' or 'https://'.`
|
||||
);
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OCO_MODEL](value: any, config: any = {}) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OCO_MODEL,
|
||||
@@ -880,6 +890,7 @@ export type ConfigType = {
|
||||
[CONFIG_KEYS.OCO_TOKENS_MAX_INPUT]: number;
|
||||
[CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT]: number;
|
||||
[CONFIG_KEYS.OCO_API_URL]?: string;
|
||||
[CONFIG_KEYS.OCO_PROXY]?: string;
|
||||
[CONFIG_KEYS.OCO_API_CUSTOM_HEADERS]?: string;
|
||||
[CONFIG_KEYS.OCO_DESCRIPTION]: boolean;
|
||||
[CONFIG_KEYS.OCO_EMOJI]: boolean;
|
||||
@@ -964,6 +975,10 @@ const getEnvConfig = (envPath: string) => {
|
||||
return {
|
||||
OCO_MODEL: process.env.OCO_MODEL,
|
||||
OCO_API_URL: process.env.OCO_API_URL,
|
||||
OCO_PROXY:
|
||||
process.env.OCO_PROXY ||
|
||||
process.env.HTTPS_PROXY ||
|
||||
process.env.HTTP_PROXY,
|
||||
OCO_API_KEY: process.env.OCO_API_KEY,
|
||||
OCO_API_CUSTOM_HEADERS: process.env.OCO_API_CUSTOM_HEADERS,
|
||||
OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER as OCO_AI_PROVIDER_ENUM,
|
||||
@@ -1189,6 +1204,11 @@ function getConfigKeyDetails(key) {
|
||||
'Custom API URL - may be used to set proxy path to OpenAI API',
|
||||
values: ["URL string (must start with 'http://' or 'https://')"]
|
||||
};
|
||||
case CONFIG_KEYS.OCO_PROXY:
|
||||
return {
|
||||
description: 'HTTP/HTTPS Proxy URL',
|
||||
values: ["URL string (must start with 'http://' or 'https://')"]
|
||||
};
|
||||
case CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER:
|
||||
return {
|
||||
description: 'Message template placeholder',
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface AiEngineConfig {
|
||||
maxTokensOutput: number;
|
||||
maxTokensInput: number;
|
||||
baseURL?: string;
|
||||
proxy?: string;
|
||||
customHeaders?: Record<string, string>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AnthropicClient from '@anthropic-ai/sdk';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import {
|
||||
MessageCreateParamsNonStreaming,
|
||||
MessageParam
|
||||
@@ -18,7 +19,15 @@ export class AnthropicEngine implements AiEngine {
|
||||
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
this.client = new AnthropicClient({ apiKey: this.config.apiKey });
|
||||
const clientOptions: any = { apiKey: this.config.apiKey };
|
||||
|
||||
const proxy =
|
||||
config.proxy || process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
||||
if (proxy) {
|
||||
clientOptions.httpAgent = new HttpsProxyAgent(proxy);
|
||||
}
|
||||
|
||||
this.client = new AnthropicClient(clientOptions);
|
||||
}
|
||||
|
||||
public generateCommitMessage = async (
|
||||
|
||||
@@ -29,10 +29,15 @@ export class GeminiEngine implements AiEngine {
|
||||
.map((m) => m.content)
|
||||
.join('\n');
|
||||
|
||||
const gemini = this.client.getGenerativeModel({
|
||||
model: this.config.model,
|
||||
systemInstruction
|
||||
});
|
||||
const gemini = this.client.getGenerativeModel(
|
||||
{
|
||||
model: this.config.model,
|
||||
systemInstruction
|
||||
},
|
||||
{
|
||||
baseUrl: this.config.baseURL
|
||||
}
|
||||
);
|
||||
|
||||
const contents = messages
|
||||
.filter((m) => m.role !== 'system')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { OpenAI } from 'openai';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff';
|
||||
import { normalizeEngineError } from '../utils/engineErrorHandler';
|
||||
import { removeContentTags } from '../utils/removeContentTags';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { OpenAI } from 'openai';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff';
|
||||
import { parseCustomHeaders } from '../utils/engine';
|
||||
import { normalizeEngineError } from '../utils/engineErrorHandler';
|
||||
@@ -23,6 +24,12 @@ export class OpenAiEngine implements AiEngine {
|
||||
clientOptions.baseURL = config.baseURL;
|
||||
}
|
||||
|
||||
const proxy =
|
||||
config.proxy || process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
||||
if (proxy) {
|
||||
clientOptions.httpAgent = new HttpsProxyAgent(proxy);
|
||||
}
|
||||
|
||||
if (config.customHeaders) {
|
||||
const headers = parseCustomHeaders(config.customHeaders);
|
||||
if (Object.keys(headers).length > 0) {
|
||||
|
||||
@@ -47,6 +47,7 @@ export function getEngine(): AiEngine {
|
||||
maxTokensOutput: config.OCO_TOKENS_MAX_OUTPUT!,
|
||||
maxTokensInput: config.OCO_TOKENS_MAX_INPUT!,
|
||||
baseURL: config.OCO_API_URL!,
|
||||
proxy: config.OCO_PROXY!,
|
||||
apiKey: config.OCO_API_KEY!,
|
||||
customHeaders
|
||||
};
|
||||
|
||||
21
src/utils/proxy.ts
Normal file
21
src/utils/proxy.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { setGlobalDispatcher, ProxyAgent } from 'undici';
|
||||
import axios from 'axios';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
|
||||
export function setupProxy(proxyUrl?: string) {
|
||||
const proxy = proxyUrl || process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
||||
if (proxy) {
|
||||
try {
|
||||
// Set global dispatcher for undici (affects globalThis.fetch used by Gemini and others)
|
||||
const dispatcher = new ProxyAgent(proxy);
|
||||
setGlobalDispatcher(dispatcher);
|
||||
|
||||
// Set axios global agent
|
||||
const agent = new HttpsProxyAgent(proxy);
|
||||
axios.defaults.httpsAgent = agent;
|
||||
axios.defaults.proxy = false; // Disable axios built-in proxy handling to use agent
|
||||
} catch (error) {
|
||||
console.warn(`[Proxy Error] Failed to set proxy: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user