diff --git a/local/ai-provider/.luarc.json b/local/ai-provider/.luarc.json new file mode 100644 index 0000000..8a8b5be --- /dev/null +++ b/local/ai-provider/.luarc.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + "runtime.version": "LuaJIT", + "runtime.path": [ + "lua/?.lua", + "lua/?/init.lua" + ], + "diagnostics.globals": [ + "vim" + ], + "workspace.checkThirdParty": false, + "workspace.library": [ + "/home/misti/.local/share/nvim/lazy/plenary.nvim", + "/usr/share/nvim/runtime/lua", + "/usr/share/nvim/runtime/lua/vim" + ], + "telemetry.enable": false +} diff --git a/local/ai-provider/lua/ai-provider/core.lua b/local/ai-provider/lua/ai-provider/core.lua index 47eee0e..5c54a33 100644 --- a/local/ai-provider/lua/ai-provider/core.lua +++ b/local/ai-provider/lua/ai-provider/core.lua @@ -12,6 +12,14 @@ local state = { model_cache = {}, } +local function valid_source_id(source_id) + return type(source_id) == 'string' and source_id ~= '' +end + +local function model_display(provider, model) + return provider .. '/' .. model +end + local function is_configured_provider(provider) return type(state.config.providers[provider]) == 'table' end @@ -88,10 +96,6 @@ function M.get_provider(name) return providers[name] end -function M.get_provider_implementation(name) - return providers[name] -end - function M.get_provider_config(name) if not is_configured_provider(name) then return nil @@ -129,8 +133,19 @@ function M.set_default_provider(provider) return M.save_preferences(prefs) end -function M.get_selected_model(provider) +function M.get_selected_model(provider, source_id) local prefs = M.load_preferences() + if valid_source_id(source_id) then + local source = prefs.sources and prefs.sources[source_id] + if type(source) == 'table' + and source.provider == provider + and type(source.model) == 'string' + and source.model ~= '' + then + return source.model + end + end + local provider_prefs = prefs[provider] if type(provider_prefs) == 'table' and type(provider_prefs.model) == 'string' and provider_prefs.model ~= '' then return provider_prefs.model @@ -155,6 +170,71 @@ function M.set_selected_model(provider, model) return M.save_preferences(prefs) end +function M.get_source_selection(source_id) + if not valid_source_id(source_id) then + return nil + end + + local prefs = M.load_preferences() + local source = prefs.sources and prefs.sources[source_id] + if type(source) == 'table' and is_configured_provider(source.provider) and type(source.model) == 'string' and source.model ~= '' then + return { provider = source.provider, model = source.model, label = model_display(source.provider, source.model) } + end + + local provider = M.get_default_provider() + local model = provider and M.get_selected_model(provider) or nil + if provider and model then + return { provider = provider, model = model, label = model_display(provider, model) } + end + + return nil +end + +function M.set_source_selection(source_id, provider, model) + if not valid_source_id(source_id) or not is_configured_provider(provider) or type(model) ~= 'string' or model == '' then + return false + end + + local prefs = M.load_preferences() + prefs.sources = prefs.sources or {} + prefs.sources[source_id] = { provider = provider, model = model, label = model_display(provider, model) } + return M.save_preferences(prefs) +end + +function M.register_source(source_id, opts) + if not valid_source_id(source_id) then + return false + end + + opts = opts or {} + local prefs = M.load_preferences() + prefs.sources = prefs.sources or {} + if type(prefs.sources[source_id]) == 'table' then + return true + end + + local provider = opts.provider or M.get_default_provider() + local model = opts.model or (provider and M.get_selected_model(provider) or nil) + if provider and model and is_configured_provider(provider) then + prefs.sources[source_id] = { provider = provider, model = model, label = model_display(provider, model) } + else + prefs.sources[source_id] = {} + end + return M.save_preferences(prefs) +end + +function M.list_sources() + local prefs = M.load_preferences() + local sources = {} + for source_id in pairs(prefs.sources or {}) do + if type(source_id) == 'string' then + table.insert(sources, source_id) + end + end + table.sort(sources) + return sources +end + function M.check(provider, callback, opts) local implementation = M.get_provider(provider) if not implementation or not implementation.check then @@ -223,20 +303,28 @@ function M.chat(first, second) return nil end - request.model = request.model or M.get_selected_model(provider) + request.model = request.model or M.get_selected_model(provider, request.source_id) request.provider_config = get_provider_config(provider) if not request.model then - log.error('chat requested without selected model for provider: ' .. tostring(provider)) + log.error( + string.format('chat requested without selected model source=%s provider=%s', tostring(request.source_id), tostring(provider)) + ) if request.callback then request.callback(nil, nil) end - vim.notify('No model selected for ' .. provider .. '. Run :AIProvider ' .. provider .. ' model first.', vim.log.levels.ERROR) + local source_hint = valid_source_id(request.source_id) and ('source ' .. request.source_id .. ' ') or provider .. ' ' + vim.notify('No model selected for ' .. source_hint .. '. Run :AIProvider source ' .. (request.source_id or '') .. ' model first.', vim.log.levels.ERROR) return nil end + if valid_source_id(request.source_id) then + M.set_source_selection(request.source_id, provider, request.model) + end + log.info( string.format( - 'chat start provider=%s model=%s prompt_chars=%d max_tokens=%s context_size=%s stream=%s', + 'chat start source=%s provider=%s model=%s prompt_chars=%d max_tokens=%s context_size=%s stream=%s', + tostring(request.source_id), provider, request.model, type(request.prompt) == 'string' and #request.prompt or 0, @@ -252,10 +340,6 @@ function M.chat_with(provider, request) return M.chat(provider, request) end -local function model_display(provider, model) - return provider .. '/' .. model -end - local function collect_models(callback) local all = {} local provider_names = M.list_providers() @@ -348,7 +432,7 @@ function M.select_model(provider) end, { force = true }) end -function M.select_helper(opts, callback) +local function select_helper(opts, callback) if type(opts) == 'function' then callback = opts opts = {} @@ -364,7 +448,7 @@ function M.select_helper(opts, callback) return end - local current = opts.current + local current = opts.current or (valid_source_id(opts.source_id) and M.get_source_selection(opts.source_id) or nil) vim.ui.select(models, { prompt = opts.prompt or 'Select AI provider model:', format_item = function(item) @@ -387,7 +471,42 @@ function M.select_helper(opts, callback) end) end -local global_actions = { 'default', 'model', 'models' } +function M.select_source_model(source_id) + if valid_source_id(source_id) then + select_helper({ + prompt = 'Select AI model for ' .. source_id .. ':', + source_id = source_id, + }, function(choice) + if not choice then + return + end + M.set_source_selection(source_id, choice.provider, choice.model) + vim.notify('AI model for ' .. source_id .. ' set to ' .. choice.label, vim.log.levels.INFO) + end) + return + end + + local sources = M.list_sources() + if #sources == 0 then + vim.notify('No AI provider sources have been seen yet.', vim.log.levels.WARN) + return + end + + vim.ui.select(sources, { + prompt = 'Select AI provider source:', + format_item = function(item) + local selection = M.get_source_selection(item) + local suffix = selection and (' (' .. selection.label .. ')') or '' + return item .. suffix + end, + }, function(choice) + if choice then + M.select_source_model(choice) + end + end) +end + +local global_actions = { 'default', 'model', 'models', 'source', 'sources' } local provider_actions = { 'auth', 'check', 'model', 'models' } local function starts_with(value, prefix) @@ -440,6 +559,30 @@ function M.command_complete(arglead, cmdline) return {} end + if first_arg == 'sources' then + return {} + end + + if first_arg == 'source' then + if argc == 2 then + return filter(M.list_sources(), arglead) + end + if argc == 3 then + return filter({ 'model' }, arglead) + end + if parts[4] == 'model' then + local cached = {} + for _, provider in ipairs(M.list_providers()) do + for _, model in ipairs(state.model_cache[provider] or {}) do + table.insert(cached, model_display(provider, model)) + end + end + table.sort(cached) + return filter(cached, arglead) + end + return {} + end + if not providers[first_arg] then return {} end @@ -492,6 +635,43 @@ function M.run_command(args) return end + if args[1] == 'sources' then + local lines = vim.tbl_map(function(source_id) + local selection = M.get_source_selection(source_id) + local suffix = selection and (' ' .. selection.label) or '' + return source_id .. suffix + end, M.list_sources()) + print_lines(lines) + return + end + + if args[1] == 'source' then + local source_id = args[2] + if not valid_source_id(source_id) then + vim.notify('Expected source ID, for example :AIProvider source ai-commit model', vim.log.levels.ERROR) + return + end + + local action = args[3] or 'model' + if action ~= 'model' then + vim.notify('Unknown AIProvider source action: ' .. action, vim.log.levels.ERROR) + return + end + + if args[4] then + local provider, model = args[4]:match '^([^/]+)/(.+)$' + if not provider or not model or not is_configured_provider(provider) then + vim.notify('Expected source model as provider/model, for example ollama/gemma4:e2b', vim.log.levels.ERROR) + return + end + M.set_source_selection(source_id, provider, model) + vim.notify('AI model for ' .. source_id .. ' set to ' .. model_display(provider, model), vim.log.levels.INFO) + else + M.select_source_model(source_id) + end + return + end + if args[1] == 'default' then if args[2] then if M.set_default_provider(args[2]) then diff --git a/local/ai-provider/lua/ai-provider/curl.lua b/local/ai-provider/lua/ai-provider/curl.lua index 016d9d5..7a39954 100644 --- a/local/ai-provider/lua/ai-provider/curl.lua +++ b/local/ai-provider/lua/ai-provider/curl.lua @@ -2,10 +2,17 @@ local M = {} local DEFAULT_TIMEOUT = 30000 +local function unpack_values(values, index, last) + if index > last then + return + end + return values[index], unpack_values(values, index + 1, last) +end + local function schedule(callback, ...) - local args = { ... } + local args = { n = select('#', ...), ... } vim.schedule(function() - callback(unpack(args)) + callback(unpack_values(args, 1, args.n)) end) end diff --git a/local/ai-provider/lua/ai-provider/init.lua b/local/ai-provider/lua/ai-provider/init.lua index 7cb9e95..68c9ceb 100644 --- a/local/ai-provider/lua/ai-provider/init.lua +++ b/local/ai-provider/lua/ai-provider/init.lua @@ -2,6 +2,7 @@ ---@class AiProviderPreferences ---@field default_provider? string Default provider used by chat calls without an explicit provider. +---@field sources? table Feature-specific provider/model preferences keyed by caller ID. ---@field [string] table Provider-specific preferences. Provider tables currently support `model`. ---@class AiProviderProviderConfig @@ -13,15 +14,11 @@ ---@field think? boolean Optional Ollama thinking mode override for reasoning models. ---@field models? table Optional logical model profiles. Keys are selectable model names. ----@class AiProviderHelperConfig ----@field provider string Provider name used by the helper. ----@field model string Model/profile name returned by the helper. This is a reference to the configured model name, not copied options. +---@class AiProviderSelection +---@field provider string Provider name. +---@field model string Model/profile name. This is a reference to the configured model name, not copied options. ---@field label? string Display label, usually `provider/model`. ----@class AiProviderSelectHelperOptions ----@field prompt? string Picker prompt. ----@field current? AiProviderHelperConfig Current selection used for picker highlighting. - ---@class AiProviderModelConfig ---@field model string Underlying provider model name. ---@field context_size? integer Optional model-specific context size override. @@ -55,6 +52,7 @@ ---@field elapsed_ms? number Elapsed duration in milliseconds. ---@class AiProviderChatRequest +---@field source_id? string Caller/source ID, for example `ai-commit`. Used for logging and source-specific model preferences. ---@field provider? string Provider name. Defaults to `get_default_provider()`. ---@field model? string Model name. Defaults to the selected model for the provider. ---@field prompt string User prompt to send to the provider. @@ -76,29 +74,12 @@ local M = {} local core = require 'ai-provider.core' ----@return AiProviderPreferences prefs Saved provider preferences. -function M.load_preferences() - return core.load_preferences() -end - ----@param prefs AiProviderPreferences ----@return boolean saved Whether preferences were written. -function M.save_preferences(prefs) - return core.save_preferences(prefs) -end - ---@param name string Provider name, for example `ollama`. ---@return table|nil provider Active provider implementation table. Returns nil for known but unconfigured providers. function M.get_provider(name) return core.get_provider(name) end ----@param name string Provider name, for example `ollama`. ----@return table|nil provider Provider implementation table, even when not enabled by config. -function M.get_provider_implementation(name) - return core.get_provider_implementation(name) -end - ---@param name string Provider name, for example `ollama`. ---@return AiProviderProviderConfig|nil config Active provider config. function M.get_provider_config(name) @@ -115,11 +96,6 @@ function M.get_default_provider() return core.get_default_provider() end ----@return string|nil provider Current/default provider name, or nil before valid setup. -function M.get_current_provider() - return core.get_default_provider() -end - ---@param provider string Provider name. ---@return boolean saved Whether the provider was saved as default. function M.set_default_provider(provider) @@ -127,21 +103,10 @@ function M.set_default_provider(provider) end ---@param provider string Provider name. ----@return boolean saved Whether the provider was saved as default. -function M.set_provider(provider) - return core.set_default_provider(provider) -end - ----@param provider string Provider name. +---@param source_id? string Caller/source ID. ---@return string|nil model Selected model for the provider. -function M.get_selected_model(provider) - return core.get_selected_model(provider) -end - ----@param provider string Provider name. ----@return string|nil model Selected model for the provider. -function M.get_model(provider) - return core.get_selected_model(provider) +function M.get_selected_model(provider, source_id) + return core.get_selected_model(provider, source_id) end ---@param provider string Provider name. @@ -151,11 +116,30 @@ function M.set_selected_model(provider, model) return core.set_selected_model(provider, model) end +---@param source_id string Caller/source ID. +---@return AiProviderSelection|nil selection Source-specific selection. +function M.get_source_selection(source_id) + return core.get_source_selection(source_id) +end + +---@param source_id string Caller/source ID. ---@param provider string Provider name. ---@param model string Model name. ----@return boolean saved Whether the model preference was saved. -function M.set_model(provider, model) - return core.set_selected_model(provider, model) +---@return boolean saved Whether the source preference was saved. +function M.set_source_selection(source_id, provider, model) + return core.set_source_selection(source_id, provider, model) +end + +---@return string[] sources Known source IDs. +function M.list_sources() + return core.list_sources() +end + +---@param source_id string Caller/source ID. +---@param opts? AiProviderSelection Optional initial provider/model selection. +---@return boolean saved Whether the source was registered. +function M.register_source(source_id, opts) + return core.register_source(source_id, opts) end ---@param provider string Provider name. @@ -200,12 +184,9 @@ function M.select_model(provider) return core.select_model(provider) end ----Open a model picker for feature-specific callers. This does not persist state; ----callers should save the returned provider/model reference themselves. ----@param opts? AiProviderSelectHelperOptions|fun(selection: AiProviderHelperConfig|nil) ----@param callback? fun(selection: AiProviderHelperConfig|nil) Called with the selected provider/model reference. -function M.select_helper(opts, callback) - return core.select_helper(opts, callback) +---@param source_id? string Caller/source ID. Omit to pick from known sources first. +function M.select_source_model(source_id) + return core.select_source_model(source_id) end ---@param arglead string Current command-line argument prefix. @@ -228,6 +209,8 @@ end ---- `:AIProvider` opens the all-provider model picker. ---- `:AIProvider model` opens the all-provider model picker. ---- `:AIProvider model provider/model` sets the default provider and that provider's default model. +---- `:AIProvider sources` lists known caller/source IDs. +---- `:AIProvider source model [provider/model]` picks or sets the model for one caller/source ID. ---- `:AIProvider default [provider]` shows or sets the default provider. ---- `:AIProvider model [model]` picks or sets only that provider's default model. ---@param opts AiProviderConfig diff --git a/local/ai-provider/tests/ai_provider/core_spec.lua b/local/ai-provider/tests/ai_provider/core_spec.lua index 20d4588..c0bd127 100644 --- a/local/ai-provider/tests/ai_provider/core_spec.lua +++ b/local/ai-provider/tests/ai_provider/core_spec.lua @@ -11,7 +11,6 @@ describe('ai-provider core', function() assert.are.same({ 'ollama' }, ai_provider.list_providers()) assert.is_truthy(ai_provider.get_provider 'ollama') - assert.is_truthy(ai_provider.get_provider_implementation 'ollama') assert.is_nil(ai_provider.get_provider 'codex') end) @@ -27,7 +26,7 @@ describe('ai-provider core', function() assert.matches('default_provider must be configured', err) end) - it('select helper returns a provider/model reference without saving it globally', function() + it('selects and saves a model for a source id', function() ai_provider.setup { default_provider = 'ollama', providers = { @@ -36,9 +35,19 @@ describe('ai-provider core', function() } local core = require 'ai-provider.core' + local original_load_preferences = core.load_preferences + local original_save_preferences = core.save_preferences local original_list_models = core.list_models local original_select = vim.ui.select - local selected = nil + local prefs = {} + + rawset(core, 'load_preferences', function() + return vim.deepcopy(prefs) + end) + rawset(core, 'save_preferences', function(next_prefs) + prefs = vim.deepcopy(next_prefs) + return true + end) rawset(core, 'list_models', function(provider, callback) callback(provider == 'ollama' and { 'gemma4:e2b 64k' } or {}) @@ -47,14 +56,45 @@ describe('ai-provider core', function() on_choice(items[1]) end) - ai_provider.select_helper(function(choice) - selected = choice - end) + assert.is_true(ai_provider.register_source 'ai-commit') + ai_provider.select_source_model 'ai-commit' + rawset(core, 'load_preferences', original_load_preferences) + rawset(core, 'save_preferences', original_save_preferences) rawset(core, 'list_models', original_list_models) rawset(vim.ui, 'select', original_select) - assert.are.same({ provider = 'ollama', model = 'gemma4:e2b 64k', label = 'ollama/gemma4:e2b 64k' }, selected) - assert.are.same('gemma4:e2b', ai_provider.get_selected_model 'ollama') + assert.are.same({ provider = 'ollama', model = 'gemma4:e2b 64k', label = 'ollama/gemma4:e2b 64k' }, prefs.sources['ai-commit']) + end) + + it('stores model preferences per source id', function() + ai_provider.setup { + default_provider = 'ollama', + providers = { + ollama = { default_model = 'gemma4:e2b' }, + }, + } + + local core = require 'ai-provider.core' + local original_load_preferences = core.load_preferences + local original_save_preferences = core.save_preferences + local prefs = {} + + rawset(core, 'load_preferences', function() + return vim.deepcopy(prefs) + end) + rawset(core, 'save_preferences', function(next_prefs) + prefs = vim.deepcopy(next_prefs) + return true + end) + + assert.is_true(ai_provider.register_source 'ai-commit') + assert.are.same({ 'ai-commit' }, ai_provider.list_sources()) + assert.is_true(ai_provider.set_source_selection('ai-commit', 'ollama', 'gemma4:e2b 64k')) + assert.are.same('gemma4:e2b 64k', ai_provider.get_selected_model('ollama', 'ai-commit')) + assert.are.same({ provider = 'ollama', model = 'gemma4:e2b 64k', label = 'ollama/gemma4:e2b 64k' }, ai_provider.get_source_selection 'ai-commit') + + rawset(core, 'load_preferences', original_load_preferences) + rawset(core, 'save_preferences', original_save_preferences) end) end) diff --git a/lua/plugins/ai-commit.lua b/lua/plugins/ai-commit.lua index 114557e..f966213 100644 --- a/lua/plugins/ai-commit.lua +++ b/lua/plugins/ai-commit.lua @@ -8,10 +8,6 @@ local CONFIG = { -- Use :AIProvider to configure the local provider; this is only the remote fallback model. model = nil, -- nil = auto (use Copilot's default) model_name = nil, -- Friendly display name for selected model - local_provider = 'ollama', - local_model = 'gemma4:e2b 64k', - local_model_name = 'gemma4:e2b 64k', - openrouter = { endpoint = 'https://openrouter.ai/api/v1', -- API key is checked in the following environment variables: @@ -33,6 +29,7 @@ local CONFIG = { local COPILOT_AUTH_URL = 'https://api.github.com/copilot_internal/v2/token' local COPILOT_API_ENDPOINT = 'https://api.githubcopilot.com' local SPINNER_FRAMES = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' } +local AI_PROVIDER_SOURCE_ID = 'ai-commit' local COMMIT_PROMPT_TEMPLATE = [[You are a git commit message generator following Conventional Commits v1.0.0 specification. @@ -119,20 +116,11 @@ local function load_preferences() if ok and type(prefs) == 'table' then CONFIG.provider = prefs.provider or 'copilot' CONFIG.model = prefs.model - CONFIG.local_provider = prefs.local_provider or CONFIG.local_provider - CONFIG.local_model = prefs.local_model or CONFIG.local_model if type(prefs.model_name) == 'string' and prefs.model_name ~= '' then CONFIG.model_name = prefs.model_name else CONFIG.model_name = nil end - if type(prefs.local_model_name) == 'string' and prefs.local_model_name ~= '' then - CONFIG.local_model_name = prefs.local_model_name - elseif type(CONFIG.local_model) == 'string' and CONFIG.local_model ~= '' then - CONFIG.local_model_name = CONFIG.local_model - else - CONFIG.local_model_name = nil - end end end end @@ -148,9 +136,6 @@ local function save_preferences() provider = CONFIG.provider, model = CONFIG.model, model_name = CONFIG.model_name, - local_provider = CONFIG.local_provider, - local_model = CONFIG.local_model, - local_model_name = CONFIG.local_model_name, } local file = io.open(prefs_file, 'w') if file then @@ -240,12 +225,13 @@ end local function complete_ollama(full_prompt, callback, status_callback, request_context) local log = setup_logger() local ai_provider = require 'ai-provider' - local provider = CONFIG.local_provider or 'ollama' - local model = CONFIG.local_model or ai_provider.get_selected_model(provider) + local selection = ai_provider.get_source_selection(AI_PROVIDER_SOURCE_ID) + local provider = selection and selection.provider or ai_provider.get_default_provider() or 'ollama' + local model = selection and selection.model or ai_provider.get_selected_model(provider, AI_PROVIDER_SOURCE_ID) if not model then - log.error(provider .. ' is reachable but no model is selected') - vim.notify('No ' .. provider .. ' model selected. Run AI commit model selection first.', vim.log.levels.ERROR) + log.error('No AI provider model selected for source=' .. AI_PROVIDER_SOURCE_ID .. ' provider=' .. provider) + vim.notify('No AI commit model selected. Run :AIProvider source ai-commit model first.', vim.log.levels.ERROR) callback(nil, nil) return end @@ -276,6 +262,7 @@ local function complete_ollama(full_prompt, callback, status_callback, request_c end ai_provider.chat(provider, { + source_id = AI_PROVIDER_SOURCE_ID, model = model, prompt = full_prompt, stream = true, @@ -869,7 +856,8 @@ local function generate_commit_message_async(branch, recent_commits, diff, callb log.debug(string.format('Prompt built (branch=%s, commits=%d chars, diff=%d chars)', branch, #recent_commits, #diff)) local ai_provider = require 'ai-provider' - local local_provider = CONFIG.local_provider or 'ollama' + local selection = ai_provider.get_source_selection(AI_PROVIDER_SOURCE_ID) + local local_provider = selection and selection.provider or ai_provider.get_default_provider() or 'ollama' if status_callback then status_callback('Checking ' .. local_provider) end @@ -880,12 +868,12 @@ local function generate_commit_message_async(branch, recent_commits, diff, callb end if working then - log.info('Routing commit message generation to ' .. local_provider) + log.info('Routing commit message generation source=' .. AI_PROVIDER_SOURCE_ID .. ' provider=' .. local_provider) complete_ollama(full_prompt, callback, status_callback, request_context) return end - log.info(string.format('Ollama unavailable, falling back to %s provider', CONFIG.provider)) + log.info(string.format('%s unavailable for source=%s, falling back to %s provider', local_provider, AI_PROVIDER_SOURCE_ID, CONFIG.provider)) if CONFIG.provider == 'openrouter' then complete_openrouter(full_prompt, callback, status_callback, request_context) else @@ -941,20 +929,8 @@ local function select_local_model() local log = setup_logger() local ai_provider = require 'ai-provider' - ai_provider.select_helper({ - prompt = 'Select AI commit model:', - current = { provider = CONFIG.local_provider, model = CONFIG.local_model }, - }, function(choice) - if not choice then - return - end - - CONFIG.local_provider = choice.provider - CONFIG.local_model = choice.model - CONFIG.local_model_name = choice.model - log.info('AI commit local model changed to: ' .. choice.label) - save_preferences() - end) + log.info('Opening AI provider model picker for source=' .. AI_PROVIDER_SOURCE_ID) + ai_provider.select_source_model(AI_PROVIDER_SOURCE_ID) end --- Select and save a model from multiple providers @@ -1422,7 +1398,15 @@ return { load_preferences() local log = setup_logger() - log.info('Using model: ' .. get_selected_model_name()) + local ok, ai_provider = pcall(require, 'ai-provider') + if ok then + ai_provider.register_source(AI_PROVIDER_SOURCE_ID) + local selection = ai_provider.get_source_selection(AI_PROVIDER_SOURCE_ID) + if selection then + log.info('Using AI provider source=' .. AI_PROVIDER_SOURCE_ID .. ' model=' .. selection.label) + end + end + log.info('Using fallback model: ' .. get_selected_model_name()) state.ns_id = vim.api.nvim_create_namespace 'ai_commit_spinner' diff --git a/lua/plugins/ai-provider.lua b/lua/plugins/ai-provider.lua index 9a96306..9f376a2 100644 --- a/lua/plugins/ai-provider.lua +++ b/lua/plugins/ai-provider.lua @@ -39,16 +39,16 @@ return { { 'pm', function() - require('ai-provider').select_model() + require('ai-provider').select_source_model() end, - desc = 'AI [P]rovider [M]odel', + desc = 'AI [P]rovider Source [M]odel', }, { 'pM', function() - require('ai-provider').select_model 'ollama' + require('ai-provider').select_model() end, - desc = 'AI [P]rovider Ollama [M]odel', + desc = 'AI [P]rovider Default [M]odel', }, { 'pd',