Skip to content

Commit

Permalink
feat: request timeout config (#191)
Browse files Browse the repository at this point in the history
Co-authored-by: Hiroki Osame <hiroki.osame@gmail.com>
  • Loading branch information
rocktimsaikia and privatenumber committed Apr 8, 2023
1 parent 75eee29 commit 42a2a39
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 7 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions src/commands/aicommits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export default async (
config.locale,
staged.diff,
config.generate,
config.timeout,
config.proxy,
);
} finally {
Expand Down
1 change: 1 addition & 0 deletions src/commands/prepare-commit-msg-hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default () => (async () => {
config.locale,
staged!.diff,
config.generate,
config.timeout,
config.proxy,
);
} finally {
Expand Down
12 changes: 12 additions & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 7 additions & 2 deletions src/utils/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const httpsPost = async (
path: string,
headers: Record<string, string>,
json: unknown,
timeout: number,
proxy?: string,
) => new Promise<{
request: ClientRequest;
Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -60,6 +61,7 @@ const httpsPost = async (
const createChatCompletion = async (
apiKey: string,
json: CreateChatCompletionRequest,
timeout: number,
proxy?: string,
) => {
const { response, data } = await httpsPost(
Expand All @@ -69,6 +71,7 @@ const createChatCompletion = async (
Authorization: `Bearer ${apiKey}`,
},
json,
timeout,
proxy,
);

Expand Down Expand Up @@ -105,6 +108,7 @@ export const generateCommitMessage = async (
locale: string,
diff: string,
completions: number,
timeout: number,
proxy?: string,
) => {
const prompt = getPrompt(locale, diff);
Expand All @@ -126,6 +130,7 @@ export const generateCommitMessage = async (
stream: false,
n: completions,
},
timeout,
proxy,
);

Expand Down
29 changes: 29 additions & 0 deletions tests/specs/cli/commits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
});
21 changes: 20 additions & 1 deletion tests/specs/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
});
});

0 comments on commit 42a2a39

Please sign in to comment.