✨ add custom HTTP headers support via OCO_API_CUSTOM_HEADERS
Add OCO_API_CUSTOM_HEADERS variable to README, config enum, and env parsing to allow JSON string of custom headers. Validate that custom headers are valid JSON in config validator. Extend AiEngineConfig with customHeaders and pass headers to OllamaEngine and OpenAiEngine clients when creating requests. Parse custom headers in utils/engine and warn on invalid format. Add unit tests to ensure OCO_API_CUSTOM_HEADERS is handled correctly and merged from env over global config. This enables users to send additional headers such as Authorization or tracing headers with LLM API calls.
This commit is contained in:
@@ -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_API_CUSTOM_HEADERS = 'OCO_API_CUSTOM_HEADERS',
|
||||
OCO_OMIT_SCOPE = 'OCO_OMIT_SCOPE',
|
||||
OCO_GITPUSH = 'OCO_GITPUSH' // todo: deprecate
|
||||
}
|
||||
@@ -204,6 +205,22 @@ export const configValidators = {
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OCO_API_CUSTOM_HEADERS](value) {
|
||||
try {
|
||||
// Custom headers must be a valid JSON string
|
||||
if (typeof value === 'string') {
|
||||
JSON.parse(value);
|
||||
}
|
||||
return value;
|
||||
} catch (error) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OCO_API_CUSTOM_HEADERS,
|
||||
false,
|
||||
'Must be a valid JSON string of headers'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OCO_TOKENS_MAX_INPUT](value: any) {
|
||||
value = parseInt(value);
|
||||
validateConfig(
|
||||
@@ -380,6 +397,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_API_CUSTOM_HEADERS]?: string;
|
||||
[CONFIG_KEYS.OCO_DESCRIPTION]: boolean;
|
||||
[CONFIG_KEYS.OCO_EMOJI]: boolean;
|
||||
[CONFIG_KEYS.OCO_WHY]: boolean;
|
||||
@@ -462,6 +480,7 @@ const getEnvConfig = (envPath: string) => {
|
||||
OCO_MODEL: process.env.OCO_MODEL,
|
||||
OCO_API_URL: process.env.OCO_API_URL,
|
||||
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,
|
||||
|
||||
OCO_TOKENS_MAX_INPUT: parseConfigVarValue(process.env.OCO_TOKENS_MAX_INPUT),
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface AiEngineConfig {
|
||||
maxTokensOutput: number;
|
||||
maxTokensInput: number;
|
||||
baseURL?: string;
|
||||
customHeaders?: Record<string, string>;
|
||||
}
|
||||
|
||||
type Client =
|
||||
|
||||
@@ -11,11 +11,18 @@ export class OllamaEngine implements AiEngine {
|
||||
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
|
||||
// Combine base headers with custom headers
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...config.customHeaders
|
||||
};
|
||||
|
||||
this.client = axios.create({
|
||||
url: config.baseURL
|
||||
? `${config.baseURL}/${config.apiKey}`
|
||||
: 'http://localhost:11434/api/chat',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
headers
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+28
-5
@@ -14,11 +14,34 @@ export class OpenAiEngine implements AiEngine {
|
||||
constructor(config: OpenAiConfig) {
|
||||
this.config = config;
|
||||
|
||||
if (!config.baseURL) {
|
||||
this.client = new OpenAI({ apiKey: config.apiKey });
|
||||
} else {
|
||||
this.client = new OpenAI({ apiKey: config.apiKey, baseURL: config.baseURL });
|
||||
// Configuration options for the OpenAI client
|
||||
const clientOptions: any = {
|
||||
apiKey: config.apiKey
|
||||
};
|
||||
|
||||
// Add baseURL if present
|
||||
if (config.baseURL) {
|
||||
clientOptions.baseURL = config.baseURL;
|
||||
}
|
||||
|
||||
// Add custom headers if present
|
||||
if (config.customHeaders) {
|
||||
try {
|
||||
let headers = config.customHeaders;
|
||||
// If the headers are a string, try to parse them as JSON
|
||||
if (typeof config.customHeaders === 'string') {
|
||||
headers = JSON.parse(config.customHeaders);
|
||||
}
|
||||
|
||||
if (headers && typeof headers === 'object' && Object.keys(headers).length > 0) {
|
||||
clientOptions.defaultHeaders = headers;
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently ignore parsing errors
|
||||
}
|
||||
}
|
||||
|
||||
this.client = new OpenAI(clientOptions);
|
||||
}
|
||||
|
||||
public generateCommitMessage = async (
|
||||
@@ -42,7 +65,7 @@ export class OpenAiEngine implements AiEngine {
|
||||
this.config.maxTokensInput - this.config.maxTokensOutput
|
||||
)
|
||||
throw new Error(GenerateCommitMessageErrorEnum.tooMuchTokens);
|
||||
|
||||
|
||||
const completion = await this.client.chat.completions.create(params);
|
||||
|
||||
const message = completion.choices[0].message;
|
||||
|
||||
+18
-1
@@ -16,12 +16,29 @@ export function getEngine(): AiEngine {
|
||||
const config = getConfig();
|
||||
const provider = config.OCO_AI_PROVIDER;
|
||||
|
||||
// Parse custom headers if provided
|
||||
let customHeaders = {};
|
||||
if (config.OCO_API_CUSTOM_HEADERS) {
|
||||
try {
|
||||
// If it's already an object, no need to parse it
|
||||
if (typeof config.OCO_API_CUSTOM_HEADERS === 'object' && !Array.isArray(config.OCO_API_CUSTOM_HEADERS)) {
|
||||
customHeaders = config.OCO_API_CUSTOM_HEADERS;
|
||||
} else {
|
||||
// Try to parse as JSON
|
||||
customHeaders = JSON.parse(config.OCO_API_CUSTOM_HEADERS);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers');
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
model: config.OCO_MODEL!,
|
||||
maxTokensOutput: config.OCO_TOKENS_MAX_OUTPUT!,
|
||||
maxTokensInput: config.OCO_TOKENS_MAX_INPUT!,
|
||||
baseURL: config.OCO_API_URL!,
|
||||
apiKey: config.OCO_API_KEY!
|
||||
apiKey: config.OCO_API_KEY!,
|
||||
customHeaders // Add custom headers to the configuration
|
||||
};
|
||||
|
||||
switch (provider) {
|
||||
|
||||
Reference in New Issue
Block a user