feat(ai): support opencode variant setting

This commit is contained in:
Guillaume ARM 2026-06-11 23:28:41 +02:00
parent 3cea167261
commit 93a4ad2395
7 changed files with 102 additions and 5 deletions

View File

@ -9,6 +9,7 @@ local DEFAULT_LUA_EXEC_MAX_RETRIES = 2;
local DEFAULT_LUA_EXEC_TIMEOUT_SECONDS = 5; local DEFAULT_LUA_EXEC_TIMEOUT_SECONDS = 5;
local DEFAULT_SESSION_SETTING_KEY = 'opencc.session_id'; local DEFAULT_SESSION_SETTING_KEY = 'opencc.session_id';
local DEFAULT_AGENT_SETTING_KEY = 'opencc.agent'; local DEFAULT_AGENT_SETTING_KEY = 'opencc.agent';
local DEFAULT_VARIANT_SETTING_KEY = 'opencc.variant';
local createHttp = require('/apis/libhttp'); local createHttp = require('/apis/libhttp');
@ -216,6 +217,13 @@ local function createAi(opts)
return agent; return agent;
end end
local function resolveVariant(options)
local variant = options.variant;
if variant == nil then variant = settingsLib.get(DEFAULT_VARIANT_SETTING_KEY); end
if isBlank(variant) then return nil; end
return variant;
end
local function resolveConfig(options) local function resolveConfig(options)
local url = options.serverUrl or settingsLib.get('opencc.server_url'); local url = options.serverUrl or settingsLib.get('opencc.server_url');
if not url or url == '' then if not url or url == '' then
@ -233,6 +241,7 @@ local function createAi(opts)
providerID = providerId, providerID = providerId,
modelID = modelId, modelID = modelId,
agent = resolveAgent(options), agent = resolveAgent(options),
variant = resolveVariant(options),
timeoutSeconds = resolveTimeout(options), timeoutSeconds = resolveTimeout(options),
pollTimeoutSeconds = resolvePollTimeout(options), pollTimeoutSeconds = resolvePollTimeout(options),
pollIntervalSeconds = resolvePollInterval(options), pollIntervalSeconds = resolvePollInterval(options),
@ -250,6 +259,9 @@ local function createAi(opts)
if cfg.agent then if cfg.agent then
body.agent = cfg.agent; body.agent = cfg.agent;
end end
if cfg.variant then
body.variant = cfg.variant;
end
return body; return body;
end end
@ -263,6 +275,9 @@ local function createAi(opts)
if cfg.agent then if cfg.agent then
body.agent = cfg.agent; body.agent = cfg.agent;
end end
if cfg.variant then
body.variant = cfg.variant;
end
return body; return body;
end end
@ -677,6 +692,7 @@ local function createAi(opts)
providerID = options.providerID, providerID = options.providerID,
modelID = options.modelID, modelID = options.modelID,
agent = options.agent, agent = options.agent,
variant = options.variant,
timeoutSeconds = options.timeoutSeconds, timeoutSeconds = options.timeoutSeconds,
}; };
end end

View File

@ -1,6 +1,6 @@
{ {
"name": "TrapOS", "name": "TrapOS",
"version": "0.8.15", "version": "0.8.16",
"branch": "next", "branch": "next",
"packages": [ "packages": [
"trapos" "trapos"

View File

@ -5,8 +5,8 @@
"trapos-boot": "0.3.2", "trapos-boot": "0.3.2",
"trapos-net": "0.3.0", "trapos-net": "0.3.0",
"trapos-ui": "0.2.2", "trapos-ui": "0.2.2",
"trapos-ai": "0.6.13", "trapos-ai": "0.6.14",
"trapos-sandbox": "0.2.2", "trapos-sandbox": "0.2.2",
"trapos": "0.8.15" "trapos": "0.8.16"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "trapos-ai", "name": "trapos-ai",
"version": "0.6.13", "version": "0.6.14",
"description": "TrapOS AI client for opencode serve", "description": "TrapOS AI client for opencode serve",
"dependencies": ["trapos-core"], "dependencies": ["trapos-core"],
"files": [ "files": [

View File

@ -1,6 +1,6 @@
{ {
"name": "trapos", "name": "trapos",
"version": "0.8.15", "version": "0.8.16",
"description": "TrapOS full install meta-package", "description": "TrapOS full install meta-package",
"dependencies": [ "dependencies": [
"trapos-boot", "trapos-boot",

View File

@ -50,6 +50,7 @@ local function printUsage()
print(' opencc.session_id (auto-managed)'); print(' opencc.session_id (auto-managed)');
print(' opencc.directory (optional session list scope)'); print(' opencc.directory (optional session list scope)');
print(' opencc.agent (e.g. atm10-expert)'); print(' opencc.agent (e.g. atm10-expert)');
print(' opencc.variant (e.g. low)');
print(' opencc.provider_id (e.g. anthropic)'); print(' opencc.provider_id (e.g. anthropic)');
print(' opencc.model_id (e.g. claude-opus-4-7)'); print(' opencc.model_id (e.g. claude-opus-4-7)');
print(' opencc.timeout_seconds (per HTTP call, max 60)'); print(' opencc.timeout_seconds (per HTTP call, max 60)');

View File

@ -588,6 +588,42 @@ testlib.test('ask omits blank agent setting', function()
testlib.assertEquals(body.agent, nil); testlib.assertEquals(body.agent, nil);
end); end);
testlib.test('ask includes variant when setting is set', function()
local httpStub = fakeHttp(
{ messageResp('reply') },
{}
);
local settingsStub = fakeSettings({
['opencc.server_url'] = 'http://host',
['opencc.session_id'] = 'ses_1',
['opencc.variant'] = 'low',
});
local ai = createAi({ http = httpStub, settings = settingsStub });
ai.ask('hello');
local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(body.variant, 'low');
end);
testlib.test('ask omits blank variant setting', function()
local httpStub = fakeHttp(
{ messageResp('reply') },
{}
);
local settingsStub = fakeSettings({
['opencc.server_url'] = 'http://host',
['opencc.session_id'] = 'ses_1',
['opencc.variant'] = ' ',
});
local ai = createAi({ http = httpStub, settings = settingsStub });
ai.ask('hello');
local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(body.variant, nil);
end);
testlib.test('ask includes model when provider_id and model_id are set', function() testlib.test('ask includes model when provider_id and model_id are set', function()
local httpStub = fakeHttp( local httpStub = fakeHttp(
{ messageResp('reply') }, { messageResp('reply') },
@ -681,6 +717,24 @@ testlib.test('ask options providerID/modelID override settings', function()
testlib.assertEquals(body.model.modelID, 'gpt-5'); testlib.assertEquals(body.model.modelID, 'gpt-5');
end); end);
testlib.test('ask options variant overrides setting', function()
local httpStub = fakeHttp(
{ messageResp('reply') },
{}
);
local settingsStub = fakeSettings({
['opencc.server_url'] = 'http://host',
['opencc.session_id'] = 'ses_1',
['opencc.variant'] = 'high',
});
local ai = createAi({ http = httpStub, settings = settingsStub });
ai.ask('hello', { variant = 'low' });
local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(body.variant, 'low');
end);
testlib.test('ask generates opencode-compatible message ids', function() testlib.test('ask generates opencode-compatible message ids', function()
local httpStub = fakeHttp( local httpStub = fakeHttp(
{ messageResp('reply') }, { messageResp('reply') },
@ -727,6 +781,32 @@ testlib.test('ask includes agent in async prompts', function()
testlib.assertEquals(body.agent, 'atm10-expert'); testlib.assertEquals(body.agent, 'atm10-expert');
end); end);
testlib.test('ask includes variant in async prompts', function()
local httpStub = fakeHttp(
{ asyncResp() },
{
messageListResp({ assistantMessage('msg_1', 'reply', true) }),
}
);
local settingsStub = fakeAsyncSettings({
['opencc.session_id'] = 'ses_1',
['opencc.variant'] = 'low',
});
local elFactory = fakeEventloopFactory();
local ai = createAi({
http = httpStub,
settings = settingsStub,
now = function() return 10; end,
eventloop = elFactory,
});
local ok = ai.ask('hello', { messageId = 'msg_1' });
testlib.assertTrue(ok);
local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(body.variant, 'low');
end);
testlib.test('ask includes caller context in async prompts', function() testlib.test('ask includes caller context in async prompts', function()
local httpStub = fakeHttp( local httpStub = fakeHttp(
{ asyncResp() }, { asyncResp() },