mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-12 23:02:45 +00:00
fix: remove default wiki llm timeout ceiling
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/af129945-1e0e-4d94-8676-877470c4574c
This commit is contained in:
parent
cc7a450e4d
commit
d05602112e
5 changed files with 68 additions and 10 deletions
|
|
@ -725,7 +725,7 @@ gitnexus wiki --force
|
|||
|
||||
|
||||
# Increase the timeout or retries for large codebase or slow LLM providers
|
||||
gitnexus wiki --timeout <seconds> # Per-attempt LLM request timeout in seconds (default: 60)
|
||||
gitnexus wiki --timeout <seconds> # Per-attempt LLM request timeout in seconds (default: disabled)
|
||||
gitnexus wiki --retries <n> # Max LLM retry attempts per request (default: 3)
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ Generates repository documentation from the knowledge graph using an LLM. Requir
|
|||
| `--api-key <key>` | LLM API key |
|
||||
| `--concurrency <n>` | Parallel LLM calls (default: 3) |
|
||||
| `--gist` | Publish wiki as a public GitHub Gist |
|
||||
| `--timeout <seconds>` | Per-attempt LLM request timeout in seconds (default: 60) |
|
||||
| `--timeout <seconds>` | Per-attempt LLM request timeout in seconds (default: disabled) |
|
||||
| `--retries <n>` | Max LLM retry attempts per request (default: 3) |
|
||||
|
||||
### list — Show all indexed repos
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ program
|
|||
)
|
||||
.option('--no-reasoning-model', 'Disable reasoning model mode (overrides saved config)')
|
||||
.option('--concurrency <n>', 'Parallel LLM calls (default: 3)', '3')
|
||||
.option('--timeout <seconds>', 'Per-attempt LLM request timeout in seconds (default: 60)')
|
||||
.option('--timeout <seconds>', 'Per-attempt LLM request timeout in seconds (default: disabled)')
|
||||
.option('--retries <n>', 'Max LLM retry attempts per request (default: 3)')
|
||||
.option('--gist', 'Publish wiki as a public GitHub Gist after generation')
|
||||
.option('-v, --verbose', 'Enable verbose output (show LLM commands and responses)')
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export interface LLMConfig {
|
|||
apiVersion?: string;
|
||||
/** When true, strips sampling params and uses max_completion_tokens instead of max_tokens */
|
||||
isReasoningModel?: boolean;
|
||||
/** Per-attempt fetch timeout in ms (default: 60_000). */
|
||||
/** Per-attempt fetch timeout in ms. Omit to disable request timeouts. */
|
||||
requestTimeoutMs?: number;
|
||||
/** Max fetch attempts before giving up (default: 3). */
|
||||
maxAttempts?: number;
|
||||
|
|
@ -237,12 +237,13 @@ export async function callLLM(
|
|||
...authHeaders,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
// Per-attempt timeout. Without this each retry can hang
|
||||
// indefinitely on a frozen TCP connection — the per-call
|
||||
// signal is the only timeout `resilientFetch` honors;
|
||||
// `capDelayMs` only bounds the *backoff* between attempts.
|
||||
// Default 60s; raise via --timeout for slow models or large pages.
|
||||
signal: AbortSignal.timeout(config.requestTimeoutMs ?? 60_000),
|
||||
// Per-attempt timeout is opt-in for wiki generation. Large local
|
||||
// model runs can legitimately take well over a minute, so the
|
||||
// default runtime path must not impose a hidden 60s ceiling.
|
||||
signal:
|
||||
config.requestTimeoutMs !== undefined
|
||||
? AbortSignal.timeout(config.requestTimeoutMs)
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
breakerKey: `wiki-llm-${new URL(url).host}`,
|
||||
|
|
|
|||
|
|
@ -237,6 +237,63 @@ describe('callLLM — reasoning model params', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('callLLM — timeout handling', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('does not apply a default timeout when requestTimeoutMs is omitted', async () => {
|
||||
const fetchSpy = vi.fn().mockResolvedValue(
|
||||
new Response(JSON.stringify({ choices: [{ message: { content: 'answer' } }], usage: {} }), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal('fetch', fetchSpy);
|
||||
const timeoutSpy = vi.spyOn(AbortSignal, 'timeout');
|
||||
|
||||
const { callLLM } = await import('../../src/core/wiki/llm-client.js');
|
||||
await callLLM('test', {
|
||||
apiKey: 'sk-test',
|
||||
baseUrl: 'https://api.openai.com/v1',
|
||||
model: 'gpt-4o',
|
||||
maxTokens: 500,
|
||||
temperature: 0,
|
||||
});
|
||||
|
||||
expect(timeoutSpy).not.toHaveBeenCalled();
|
||||
const [, init] = fetchSpy.mock.calls[0] as [string, RequestInit];
|
||||
expect(init.signal).toBeUndefined();
|
||||
});
|
||||
|
||||
it('applies an explicit timeout when requestTimeoutMs is provided', async () => {
|
||||
const fetchSpy = vi.fn().mockResolvedValue(
|
||||
new Response(JSON.stringify({ choices: [{ message: { content: 'answer' } }], usage: {} }), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal('fetch', fetchSpy);
|
||||
const timeoutSignal = new AbortController().signal;
|
||||
const timeoutSpy = vi.spyOn(AbortSignal, 'timeout').mockReturnValue(timeoutSignal);
|
||||
|
||||
const { callLLM } = await import('../../src/core/wiki/llm-client.js');
|
||||
await callLLM('test', {
|
||||
apiKey: 'sk-test',
|
||||
baseUrl: 'https://api.openai.com/v1',
|
||||
model: 'gpt-4o',
|
||||
maxTokens: 500,
|
||||
temperature: 0,
|
||||
requestTimeoutMs: 120_000,
|
||||
});
|
||||
|
||||
expect(timeoutSpy).toHaveBeenCalledWith(120_000);
|
||||
const [, init] = fetchSpy.mock.calls[0] as [string, RequestInit];
|
||||
expect(init.signal).toBe(timeoutSignal);
|
||||
});
|
||||
});
|
||||
|
||||
describe('callLLM — Azure content_filter error', () => {
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue