Skip to content

Commit 2bf6cca

Browse files
author
Mihamina RKTMB
committed
feat: Support Copilot/GitHub Models Responses API and improve streaming
- Detect supported_endpoints and toggle use_responses_api; route to /responses endpoints - Add responses input/output paths, including streaming event parsing and overwrite semantics - Accumulate tool calls by id/index and safely concatenate arguments; allow string|number ids - Avoid empty on_progress updates; support skip_progress, content_overwrite, reasoning_overwrite - Fix RESOURCE_SHORT_FORMAT placeholder bug; expand generate_resource_block args - Respect top_p from options; propagate supported_endpoints in model metadata
1 parent 5791127 commit 2bf6cca

2 files changed

Lines changed: 383 additions & 136 deletions

File tree

lua/CopilotChat/client.lua

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
---@field token_max_count number
2222

2323
---@class CopilotChat.client.ToolCall
24-
---@field id number
25-
---@field index number
24+
---@field id number|string
25+
---@field index number|string
2626
---@field name string
2727
---@field arguments string
2828

@@ -53,7 +53,6 @@
5353
---@field tools boolean?
5454
---@field reasoning boolean?
5555
---@field supported_endpoints string[]?
56-
---@field responses_only boolean?
5756

5857
local log = require('plenary.log')
5958
local constants = require('CopilotChat.constants')
@@ -67,7 +66,7 @@ local orderedmap = require('CopilotChat.utils.orderedmap')
6766
local stringbuffer = require('CopilotChat.utils.stringbuffer')
6867

6968
--- Constants
70-
local RESOURCE_SHORT_FORMAT = '# %s\n```%s start_line=% end_line=%s\n%s\n```'
69+
local RESOURCE_SHORT_FORMAT = '# %s\n```%s start_line=%s end_line=%s\n%s\n```'
7170
local RESOURCE_LONG_FORMAT = '# %s\n```%s path=%s start_line=%s end_line=%s\n%s\n```'
7271
local CACHE_TTL = 300 -- 5 minutes
7372

@@ -89,7 +88,11 @@ end
8988

9089
--- Generate resource block with line numbers, truncating if necessary
9190
---@param content string
92-
---@param start_line number: The starting line number
91+
---@param mimetype string?
92+
---@param name string?
93+
---@param path string?
94+
---@param start_line number? The starting line number
95+
---@param end_line number? The ending line number
9396
---@return string
9497
local function generate_resource_block(content, mimetype, name, path, start_line, end_line)
9598
local lines = vim.split(content, '\n')
@@ -321,12 +324,27 @@ function Client:ask(opts)
321324
error('Provider not found: ' .. provider_name)
322325
end
323326

327+
local supported_endpoints = {}
328+
if type(model_config.supported_endpoints) == 'table' then
329+
supported_endpoints = model_config.supported_endpoints
330+
end
331+
local use_responses_api = false
332+
for _, endpoint in ipairs(supported_endpoints) do
333+
if endpoint == '/responses' then
334+
use_responses_api = true
335+
break
336+
end
337+
end
338+
log.debug('Responses API:', use_responses_api)
339+
324340
local options = {
325341
model = vim.tbl_extend('force', model_config, {
326342
id = opts.model:gsub(':' .. provider_name .. '$', ''),
327343
}),
328344
temperature = opts.temperature,
329345
tools = opts.tools,
346+
use_responses_api = use_responses_api,
347+
top_p = 1,
330348
}
331349

332350
local max_tokens = model_config.max_input_tokens
@@ -436,29 +454,63 @@ function Client:ask(opts)
436454

437455
if out.tool_calls then
438456
for _, tool_call in ipairs(out.tool_calls) do
439-
local val = tool_calls:get(tool_call.index)
440-
if not val then
441-
tool_calls:set(tool_call.index, tool_call)
457+
local key = tool_call.index or tool_call.id
458+
if key == nil then
459+
key = #tool_calls:keys() + 1
460+
tool_call.index = key
461+
end
462+
local arguments = tool_call.arguments or ''
463+
local existing = tool_calls:get(key)
464+
if not existing then
465+
tool_calls:set(key, {
466+
id = tool_call.id,
467+
index = tool_call.index,
468+
name = tool_call.name,
469+
arguments = arguments,
470+
})
442471
else
443-
val.arguments = val.arguments .. tool_call.arguments
472+
if tool_call.name then
473+
existing.name = tool_call.name
474+
end
475+
if tool_call.id then
476+
existing.id = tool_call.id
477+
end
478+
existing.arguments = (existing.arguments or '') .. arguments
444479
end
445480
end
446481
end
447482

448483
if out.content then
449-
response_content_buffer:put(out.content)
484+
if out.content_overwrite then
485+
response_content_buffer:set(out.content)
486+
else
487+
response_content_buffer:put(out.content)
488+
end
450489
end
451490

452491
if out.reasoning then
453-
response_reasoning_buffer:put(out.reasoning)
492+
if out.reasoning_overwrite then
493+
response_reasoning_buffer:set(out.reasoning)
494+
else
495+
response_reasoning_buffer:put(out.reasoning)
496+
end
454497
end
455498

456-
if opts.on_progress then
457-
opts.on_progress({
458-
role = constants.ROLE.ASSISTANT,
459-
content = out.content or '',
460-
reasoning = out.reasoning or '',
461-
})
499+
local skip_progress = out.skip_progress
500+
if not skip_progress and (out.content_overwrite or out.reasoning_overwrite) then
501+
skip_progress = true
502+
end
503+
504+
if opts.on_progress and not skip_progress then
505+
local progress_content = out.content or ''
506+
local progress_reasoning = out.reasoning or ''
507+
if not utils.empty(progress_content) or not utils.empty(progress_reasoning) then
508+
opts.on_progress({
509+
role = constants.ROLE.ASSISTANT,
510+
content = progress_content,
511+
reasoning = progress_reasoning,
512+
})
513+
end
462514
end
463515

464516
if out.finish_reason then

0 commit comments

Comments
 (0)