feat: request timeout config (#191)
Co-authored-by: Hiroki Osame <hiroki.osame@gmail.com>
This commit is contained in:
13
README.md
13
README.md
@@ -74,10 +74,6 @@ aicommits --generate <i> # or -g <i>
|
||||
|
||||
> Warning: this uses more tokens, meaning it costs more.
|
||||
|
||||
```sh
|
||||
aicommits --all
|
||||
```
|
||||
|
||||
### Git hook
|
||||
|
||||
You can also integrate _aicommits_ with Git via the [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) hook. This lets you use Git like you normally would, and edit the commit message before committing.
|
||||
@@ -189,6 +185,15 @@ The Chat Completions (`/v1/chat/completions`) model to use. Consult the list of
|
||||
> Tip: If you have access, try upgrading to [`gpt-4`](https://platform.openai.com/docs/models/gpt-4) for next-level code analysis. It can handle double the input size, but comes at a higher cost. Check out OpenAI's website to learn more.
|
||||
|
||||
|
||||
#### timeout
|
||||
The timeout for network requests to the OpenAI API in milliseconds.
|
||||
|
||||
Default: `10000` (10 seconds)
|
||||
|
||||
```sh
|
||||
aicommits config set timeout=20000 # 20s
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
This CLI tool runs `git diff` to grab all your latest code changes, sends them to OpenAI's GPT-3, then returns the AI generated commit message.
|
||||
|
||||
@@ -58,6 +58,7 @@ export default async (
|
||||
config.locale,
|
||||
staged.diff,
|
||||
config.generate,
|
||||
config.timeout,
|
||||
config.proxy,
|
||||
);
|
||||
} finally {
|
||||
|
||||
@@ -45,6 +45,7 @@ export default () => (async () => {
|
||||
config.locale,
|
||||
staged!.diff,
|
||||
config.generate,
|
||||
config.timeout,
|
||||
config.proxy,
|
||||
);
|
||||
} finally {
|
||||
|
||||
@@ -68,6 +68,18 @@ const configParsers = {
|
||||
|
||||
return model as TiktokenModel;
|
||||
},
|
||||
timeout(timeout?: string) {
|
||||
if (!timeout) {
|
||||
return 10_000;
|
||||
}
|
||||
|
||||
parseAssert('timeout', /^\d+$/.test(timeout), 'Must be an integer');
|
||||
|
||||
const parsed = Number(timeout);
|
||||
parseAssert('timeout', parsed >= 500, 'Must be greater than 500ms');
|
||||
|
||||
return parsed;
|
||||
},
|
||||
} as const;
|
||||
|
||||
type ConfigKeys = keyof typeof configParsers;
|
||||
|
||||
@@ -10,6 +10,7 @@ const httpsPost = async (
|
||||
path: string,
|
||||
headers: Record<string, string>,
|
||||
json: unknown,
|
||||
timeout: number,
|
||||
proxy?: string,
|
||||
) => new Promise<{
|
||||
request: ClientRequest;
|
||||
@@ -28,7 +29,7 @@ const httpsPost = async (
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(postContent),
|
||||
},
|
||||
timeout: 10_000, // 10s
|
||||
timeout,
|
||||
agent: (
|
||||
proxy
|
||||
? createHttpsProxyAgent(proxy)
|
||||
@@ -50,7 +51,7 @@ const httpsPost = async (
|
||||
request.on('error', reject);
|
||||
request.on('timeout', () => {
|
||||
request.destroy();
|
||||
reject(new KnownError('Request timed out'));
|
||||
reject(new KnownError(`Time out error: request took over ${timeout}ms. Try increasing the \`timeout\` config, or checking the OpenAI API status https://status.openai.com`));
|
||||
});
|
||||
|
||||
request.write(postContent);
|
||||
@@ -60,6 +61,7 @@ const httpsPost = async (
|
||||
const createChatCompletion = async (
|
||||
apiKey: string,
|
||||
json: CreateChatCompletionRequest,
|
||||
timeout: number,
|
||||
proxy?: string,
|
||||
) => {
|
||||
const { response, data } = await httpsPost(
|
||||
@@ -69,6 +71,7 @@ const createChatCompletion = async (
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
json,
|
||||
timeout,
|
||||
proxy,
|
||||
);
|
||||
|
||||
@@ -105,6 +108,7 @@ export const generateCommitMessage = async (
|
||||
locale: string,
|
||||
diff: string,
|
||||
completions: number,
|
||||
timeout: number,
|
||||
proxy?: string,
|
||||
) => {
|
||||
const prompt = getPrompt(locale, diff);
|
||||
@@ -126,6 +130,7 @@ export const generateCommitMessage = async (
|
||||
stream: false,
|
||||
n: completions,
|
||||
},
|
||||
timeout,
|
||||
proxy,
|
||||
);
|
||||
|
||||
|
||||
@@ -252,5 +252,34 @@ export default testSuite(({ describe }) => {
|
||||
await fixture.rm();
|
||||
});
|
||||
});
|
||||
|
||||
test('Fails on timeout', async () => {
|
||||
const { fixture, aicommits } = await createFixture({
|
||||
...files,
|
||||
'.aicommits': `${files['.aicommits']}\ntimeout=500`,
|
||||
});
|
||||
const git = await createGit(fixture.path);
|
||||
|
||||
await git('add', ['data.json']);
|
||||
|
||||
const committing = aicommits([], {
|
||||
reject: false,
|
||||
});
|
||||
|
||||
committing.stdout!.on('data', (buffer: Buffer) => {
|
||||
const stdout = buffer.toString();
|
||||
if (stdout.match('└')) {
|
||||
committing.stdin!.write('y');
|
||||
committing.stdin!.end();
|
||||
}
|
||||
});
|
||||
|
||||
const { stdout, exitCode } = await committing;
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
expect(stdout).toMatch('Time out error: request took over 500ms.');
|
||||
|
||||
await fixture.rm();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { testSuite, expect } from 'manten';
|
||||
import { createFixture } from '../utils.js';
|
||||
|
||||
export default testSuite(({ describe }) => {
|
||||
describe('config', async ({ test }) => {
|
||||
describe('config', async ({ test, describe }) => {
|
||||
const { fixture, aicommits } = await createFixture();
|
||||
const configPath = path.join(fixture.path, '.aicommits');
|
||||
const openAiToken = 'OPENAI_KEY=sk-abc';
|
||||
@@ -49,6 +49,25 @@ export default testSuite(({ describe }) => {
|
||||
expect(stderr).toBe('');
|
||||
});
|
||||
|
||||
await describe('timeout', ({ test }) => {
|
||||
test('setting invalid timeout config', async () => {
|
||||
const { stderr } = await aicommits(['config', 'set', 'timeout=abc'], {
|
||||
reject: false,
|
||||
});
|
||||
|
||||
expect(stderr).toMatch('Must be an integer');
|
||||
});
|
||||
|
||||
test('setting valid timeout config', async () => {
|
||||
const timeout = 'timeout=20000';
|
||||
await aicommits(['config', 'set', timeout]);
|
||||
|
||||
const configFile = await fs.readFile(configPath, 'utf8');
|
||||
|
||||
expect(configFile).toMatch(timeout);
|
||||
});
|
||||
});
|
||||
|
||||
await fixture.rm();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user