Honor local agent timeout defaults
This commit is contained in:
1 parent
e53c61e712
commit
75b7ca02ef
3 files changed
+26
-10
No files matched your search
+8
-7
@@ -821,13 +821,14 @@ export class TempestCommand extends EventEmitter<CommandEvents> {
|
|||||||
* GENEROUS per-dispatch wall-clock backstop (ms). If a single task dispatch stays
|
* GENEROUS per-dispatch wall-clock backstop (ms). If a single task dispatch stays
|
||||||
* in-flight longer than this, the tick loop force-resolves it as a timeout so
|
* in-flight longer than this, the tick loop force-resolves it as a timeout so
|
||||||
* pendingOrActive can reach 0 and the mission can advance/complete even when an
|
* pendingOrActive can reach 0 and the mission can advance/complete even when an
|
||||||
* operator promise wedges. Deliberately large (default 5 min) so it NEVER kills a
|
* operator promise wedges. Deliberately large (default 5 min for API models,
|
||||||
* legitimately-slow-but-working LLM task (e.g. opus planning ~165s); it only fires
|
* 30 min for local-agent backends) so it does not kill legitimately slow local
|
||||||
* on truly-hung dispatches. Override via T3MP3ST_TASK_TIMEOUT_MS.
|
* CLI work; it only fires on truly-hung dispatches. Override via
|
||||||
|
* T3MP3ST_TASK_TIMEOUT_MS.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Resolve the dispatch timeout from the environment, falling back to the 5-minute
|
* Resolve the dispatch timeout from the environment, falling back to the
|
||||||
* default. Guards against a non-numeric / non-positive override.
|
* provider-specific default. Guards against a non-numeric / non-positive override.
|
||||||
*/
|
*/
|
||||||
private static resolveTaskTimeoutMs(provider?: LLMProvider): number {
|
private static resolveTaskTimeoutMs(provider?: LLMProvider): number {
|
||||||
const DEFAULT_TASK_TIMEOUT_MS = 300000; // 5 minutes — generous backstop, not a deadline
|
const DEFAULT_TASK_TIMEOUT_MS = 300000; // 5 minutes — generous backstop, not a deadline
|
||||||
@@ -1035,8 +1036,8 @@ export class TempestCommand extends EventEmitter<CommandEvents> {
|
|||||||
* never advances, and completeMission() is never called: the mission HANGS.
|
* never advances, and completeMission() is never called: the mission HANGS.
|
||||||
*
|
*
|
||||||
* On every tick we scan the in-flight dispatches and force-resolve any that have
|
* On every tick we scan the in-flight dispatches and force-resolve any that have
|
||||||
* exceeded the GENEROUS wall-clock backstop (taskTimeoutMs, default 5 min), or
|
* exceeded the GENEROUS wall-clock backstop (taskTimeoutMs), or that exhibit
|
||||||
* that exhibit the exact wedge symptom (owning operator is `executing`/`tasked`
|
* the exact wedge symptom (owning operator is `executing`/`tasked`
|
||||||
* but its currentTask is null — i.e. it has silently dropped the task). For each
|
* but its currentTask is null — i.e. it has silently dropped the task). For each
|
||||||
* we: (1) mark the task failed/timed-out in the queue, (2) clear its dispatch
|
* we: (1) mark the task failed/timed-out in the queue, (2) clear its dispatch
|
||||||
* bookkeeping, and (3) reset the owning operator back to idle so it can take new
|
* bookkeeping, and (3) reset the owning operator back to idle so it can take new
|
||||||
|
|||||||
+2
-1
@@ -1070,7 +1070,8 @@ class LocalAgentAdapter implements LLMProviderAdapter {
|
|||||||
async chat(messages: LLMMessage[], options?: ChatOptions): Promise<LLMResponse> {
|
async chat(messages: LLMMessage[], options?: ChatOptions): Promise<LLMResponse> {
|
||||||
const agentId = this.config.model || 'codex';
|
const agentId = this.config.model || 'codex';
|
||||||
const prompt = this.formatPrompt(messages, options);
|
const prompt = this.formatPrompt(messages, options);
|
||||||
const content = (await localAgentChat(agentId, prompt, { timeoutMs: this.config.timeout || 240000 })).trim();
|
const timeoutMs = typeof this.config.timeout === 'number' && this.config.timeout > 0 ? this.config.timeout : undefined;
|
||||||
|
const content = (await localAgentChat(agentId, prompt, { timeoutMs })).trim();
|
||||||
// Tool-calling over text: if the Arsenal was offered, parse the agent's tool requests so the
|
// Tool-calling over text: if the Arsenal was offered, parse the agent's tool requests so the
|
||||||
// ReAct loop EXECUTES them instead of treating this planning turn as the (abstaining) final answer.
|
// ReAct loop EXECUTES them instead of treating this planning turn as the (abstaining) final answer.
|
||||||
const toolCalls = options?.tools?.length ? parseTextToolCalls(content) : undefined;
|
const toolCalls = options?.tools?.length ? parseTextToolCalls(content) : undefined;
|
||||||
|
|||||||
+16
-2
@@ -6478,6 +6478,18 @@ function providerNeedsApiKey(provider: string): boolean {
|
|||||||
return !['codex', 'mock', 'local', 'local-agent'].includes(provider);
|
return !['codex', 'mock', 'local', 'local-agent'].includes(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readPositiveTimeoutEnv(name: string): number | undefined {
|
||||||
|
const raw = process.env[name];
|
||||||
|
if (raw == null || raw.trim() === '') return undefined;
|
||||||
|
const parsed = Number(raw);
|
||||||
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readGeneralTimeoutEnv(): number | undefined {
|
||||||
|
return readPositiveTimeoutEnv('TEMPEST_GENERAL_TIMEOUT_MS')
|
||||||
|
?? readPositiveTimeoutEnv('T3MP3ST_GENERAL_TIMEOUT_MS');
|
||||||
|
}
|
||||||
|
|
||||||
// SECURITY NOTE: `apiKey` is accepted from the request BODY here (and in the
|
// SECURITY NOTE: `apiKey` is accepted from the request BODY here (and in the
|
||||||
// mission/general routes that call this). Sending secrets in the body is not
|
// mission/general routes that call this). Sending secrets in the body is not
|
||||||
// ideal — an Authorization header is preferred — but the same-origin UI posts
|
// ideal — an Authorization header is preferred — but the same-origin UI posts
|
||||||
@@ -6501,7 +6513,9 @@ function resolveGeneralLLMConfig(provider: string, model: string | undefined, ap
|
|||||||
model: model || 'codex',
|
model: model || 'codex',
|
||||||
maxTokens: 8192,
|
maxTokens: 8192,
|
||||||
temperature: 0.4,
|
temperature: 0.4,
|
||||||
timeout: Number(process.env.TEMPEST_GENERAL_TIMEOUT_MS) || 300000,
|
timeout: readGeneralTimeoutEnv()
|
||||||
|
?? readPositiveTimeoutEnv('T3MP3ST_LOCAL_AGENT_TIMEOUT_MS')
|
||||||
|
?? 600000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const baseConfig = config.getLLMConfig(selectedProvider as any, model);
|
const baseConfig = config.getLLMConfig(selectedProvider as any, model);
|
||||||
@@ -6515,7 +6529,7 @@ function resolveGeneralLLMConfig(provider: string, model: string | undefined, ap
|
|||||||
apiKey: effectiveKey,
|
apiKey: effectiveKey,
|
||||||
maxTokens: 8192,
|
maxTokens: 8192,
|
||||||
temperature: 0.4,
|
temperature: 0.4,
|
||||||
timeout: Number(process.env.TEMPEST_GENERAL_TIMEOUT_MS) || 300000, // General planning needs room (was a hardcoded 60s); override via env
|
timeout: readGeneralTimeoutEnv() ?? 300000, // General planning needs room (was a hardcoded 60s); override via env
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user