fix(ai): cap opencode timeouts
This commit is contained in:
parent
150e847475
commit
79677e2742
@ -1,8 +1,9 @@
|
||||
local PING_PROMPT = 'reply with exactly: pong';
|
||||
|
||||
local DEFAULT_TIMEOUT_SECONDS = 120;
|
||||
local MAX_TIMEOUT_SECONDS = 120;
|
||||
local DEFAULT_TIMEOUT_SECONDS = 60;
|
||||
local MAX_TIMEOUT_SECONDS = 60;
|
||||
local DEFAULT_POLL_TIMEOUT_SECONDS = 600;
|
||||
local MAX_POLL_TIMEOUT_SECONDS = 600;
|
||||
local DEFAULT_POLL_INTERVAL_SECONDS = 2;
|
||||
local DEFAULT_LUA_EXEC_MAX_RETRIES = 2;
|
||||
local DEFAULT_LUA_EXEC_TIMEOUT_SECONDS = 5;
|
||||
@ -226,6 +227,7 @@ local function createAi(opts)
|
||||
if raw == nil then raw = settingsLib.get('opencc.poll_timeout_seconds'); end
|
||||
local n = tonumber(raw);
|
||||
if not n or n <= 0 then n = DEFAULT_POLL_TIMEOUT_SECONDS; end
|
||||
if n > MAX_POLL_TIMEOUT_SECONDS then n = MAX_POLL_TIMEOUT_SECONDS; end
|
||||
return n;
|
||||
end
|
||||
|
||||
|
||||
@ -126,7 +126,7 @@ set opencc.model_id claude-opus-4-7
|
||||
Optional timeout settings:
|
||||
|
||||
```sh
|
||||
set opencc.timeout_seconds 120
|
||||
set opencc.timeout_seconds 60
|
||||
set opencc.poll_timeout_seconds 600
|
||||
set opencc.poll_interval_seconds 2
|
||||
```
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "TrapOS",
|
||||
"version": "0.8.12",
|
||||
"version": "0.8.13",
|
||||
"branch": "next",
|
||||
"packages": [
|
||||
"trapos"
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
"trapos-boot": "0.3.2",
|
||||
"trapos-net": "0.3.0",
|
||||
"trapos-ui": "0.2.2",
|
||||
"trapos-ai": "0.6.10",
|
||||
"trapos-ai": "0.6.11",
|
||||
"trapos-sandbox": "0.2.2",
|
||||
"trapos": "0.8.12"
|
||||
"trapos": "0.8.13"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trapos-ai",
|
||||
"version": "0.6.10",
|
||||
"version": "0.6.11",
|
||||
"description": "TrapOS AI client for opencode serve",
|
||||
"dependencies": ["trapos-core"],
|
||||
"files": [
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trapos",
|
||||
"version": "0.8.12",
|
||||
"version": "0.8.13",
|
||||
"description": "TrapOS full install meta-package",
|
||||
"dependencies": [
|
||||
"trapos-boot",
|
||||
|
||||
@ -52,8 +52,8 @@ local function printUsage()
|
||||
print(' opencc.agent (e.g. atm10-expert)');
|
||||
print(' opencc.provider_id (e.g. anthropic)');
|
||||
print(' opencc.model_id (e.g. claude-opus-4-7)');
|
||||
print(' opencc.timeout_seconds (per HTTP call, max 120)');
|
||||
print(' opencc.poll_timeout_seconds (default: 600)');
|
||||
print(' opencc.timeout_seconds (per HTTP call, max 60)');
|
||||
print(' opencc.poll_timeout_seconds (default/max: 600)');
|
||||
print(' opencc.poll_interval_seconds (default: 2)');
|
||||
end
|
||||
|
||||
|
||||
64
tests/ai.lua
64
tests/ai.lua
@ -249,6 +249,20 @@ testlib.test('listSessions sends configured directory query', function()
|
||||
'http://host/session?directory=%2FUsers%2Fgarm%2Ftrap%2Fcc-libs');
|
||||
end);
|
||||
|
||||
testlib.test('listSessions caps HTTP timeout at one minute', function()
|
||||
local httpStub = fakeHttp({}, { response(200, '[]') });
|
||||
local settingsStub = fakeSettings({
|
||||
['opencc.server_url'] = 'http://host',
|
||||
['opencc.timeout_seconds'] = 120,
|
||||
});
|
||||
local ai = createAi({ http = httpStub, settings = settingsStub });
|
||||
|
||||
local ok = ai.listSessions();
|
||||
|
||||
testlib.assertTrue(ok);
|
||||
testlib.assertEquals(httpStub.getCalls[1].timeout, 60);
|
||||
end);
|
||||
|
||||
testlib.test('listSessions retries with persisted session directory when list is empty', function()
|
||||
local scopedSessions = {
|
||||
{ id = 'ses_existing', title = 'existing', time = { updated = 20 } },
|
||||
@ -847,7 +861,7 @@ testlib.test('ask polling default timeout allows ten minute replies', function()
|
||||
testlib.assertEquals(#httpStub.getCalls, 3);
|
||||
end);
|
||||
|
||||
testlib.test('ask uses two minute HTTP timeout by default', function()
|
||||
testlib.test('ask uses one minute HTTP timeout by default', function()
|
||||
local httpStub = fakeHttp(
|
||||
{ sessionResp('ses_1'), messageResp('reply') },
|
||||
{}
|
||||
@ -858,11 +872,11 @@ testlib.test('ask uses two minute HTTP timeout by default', function()
|
||||
local ok = ai.ask('hello');
|
||||
|
||||
testlib.assertTrue(ok);
|
||||
testlib.assertEquals(httpStub.postCalls[1].timeout, 120);
|
||||
testlib.assertEquals(httpStub.postCalls[2].timeout, 120);
|
||||
testlib.assertEquals(httpStub.postCalls[1].timeout, 60);
|
||||
testlib.assertEquals(httpStub.postCalls[2].timeout, 60);
|
||||
end);
|
||||
|
||||
testlib.test('ask caps per-call HTTP timeout at two minutes', function()
|
||||
testlib.test('ask caps per-call HTTP timeout at one minute', function()
|
||||
local httpStub = fakeHttp(
|
||||
{ sessionResp('ses_1'), messageResp('reply') },
|
||||
{}
|
||||
@ -876,8 +890,8 @@ testlib.test('ask caps per-call HTTP timeout at two minutes', function()
|
||||
local ok = ai.ask('hello');
|
||||
|
||||
testlib.assertTrue(ok);
|
||||
testlib.assertEquals(httpStub.postCalls[1].timeout, 120);
|
||||
testlib.assertEquals(httpStub.postCalls[2].timeout, 120);
|
||||
testlib.assertEquals(httpStub.postCalls[1].timeout, 60);
|
||||
testlib.assertEquals(httpStub.postCalls[2].timeout, 60);
|
||||
end);
|
||||
|
||||
testlib.test('ask polling times out', function()
|
||||
@ -921,6 +935,44 @@ testlib.test('ask polling times out', function()
|
||||
testlib.assertEquals(#elState.lastLoop.inspect().pending, 0);
|
||||
end);
|
||||
|
||||
testlib.test('ask caps polling timeout at ten minutes', function()
|
||||
local httpStub = fakeHttp(
|
||||
{ sessionResp('ses_1'), asyncResp() },
|
||||
{
|
||||
messageListResp({ userMessage('msg_1', 'hello'), assistantMessage('msg_2', 'partial', false) }),
|
||||
messageListResp({ userMessage('msg_1', 'hello'), assistantMessage('msg_2', 'partial', false) }),
|
||||
messageListResp({ userMessage('msg_1', 'hello'), assistantMessage('msg_2', 'partial', false) }),
|
||||
}
|
||||
);
|
||||
local settingsStub = fakeAsyncSettings({
|
||||
['opencc.poll_timeout_seconds'] = 1200,
|
||||
});
|
||||
local now = 0;
|
||||
local elFactory = fakeEventloopFactory();
|
||||
local advancingFactory = function()
|
||||
local loop = elFactory();
|
||||
local origSet = loop.setTimeout;
|
||||
loop.setTimeout = function(fn, delay)
|
||||
now = now + (delay or 0);
|
||||
return origSet(fn, delay);
|
||||
end
|
||||
return loop;
|
||||
end;
|
||||
local ai = createAi({
|
||||
http = httpStub,
|
||||
settings = settingsStub,
|
||||
now = function() return now; end,
|
||||
eventloop = advancingFactory,
|
||||
});
|
||||
|
||||
local ok, err = ai.ask('hello', { messageId = 'msg_1', pollIntervalSeconds = 300 });
|
||||
|
||||
testlib.assertTrue(not ok);
|
||||
testlib.assertTrue(string.find(err, 'delai depasse', 1, true) ~= nil);
|
||||
testlib.assertEquals(#httpStub.getCalls, 3);
|
||||
testlib.assertEquals(now, 600);
|
||||
end);
|
||||
|
||||
testlib.test('ask polling does not call os.sleep', function()
|
||||
local httpStub = fakeHttp(
|
||||
{ sessionResp('ses_1'), asyncResp() },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user