@@ -48,15 +48,30 @@ local version_headers = {
4848 .. vim .version ().patch ,
4949 [' editor-plugin-version' ] = ' CopilotChat.nvim/2.0.0' ,
5050 [' user-agent' ] = ' CopilotChat.nvim/2.0.0' ,
51+ [' sec-fetch-site' ] = ' none' ,
52+ [' sec-fetch-mode' ] = ' no-cors' ,
53+ [' sec-fetch-dest' ] = ' empty' ,
54+ [' priority' ] = ' u=4, i' ,
55+ -- ['x-github-api-version'] = '2023-07-07',
5156}
5257
5358local curl_get = async .wrap (function (url , opts , callback )
54- opts = vim .tbl_deep_extend (' force' , opts , { callback = callback })
59+ opts = vim .tbl_deep_extend (' force' , opts , {
60+ callback = callback ,
61+ on_error = function (err )
62+ callback (nil , vim .inspect (err ))
63+ end ,
64+ })
5565 curl .get (url , opts )
5666end , 3 )
5767
5868local curl_post = async .wrap (function (url , opts , callback )
59- opts = vim .tbl_deep_extend (' force' , opts , { callback = callback })
69+ opts = vim .tbl_deep_extend (' force' , opts , {
70+ callback = callback ,
71+ on_error = function (err )
72+ callback (nil , vim .inspect (err ))
73+ end ,
74+ })
6075 curl .post (url , opts )
6176end , 3 )
6277
@@ -79,7 +94,8 @@ local function machine_id()
7994 local hex_chars = ' 0123456789abcdef'
8095 local hex = ' '
8196 for _ = 1 , length do
82- hex = hex .. hex_chars :sub (math.random (1 , # hex_chars ), math.random (1 , # hex_chars ))
97+ local index = math.random (1 , # hex_chars )
98+ hex = hex .. hex_chars :sub (index , index )
8399 end
84100 return hex
85101end
@@ -235,7 +251,6 @@ local function generate_ask_request(
235251
236252 if stream then
237253 return {
238- intent = true ,
239254 model = model ,
240255 n = 1 ,
241256 stream = true ,
@@ -293,6 +308,26 @@ local Copilot = class(function(self, proxy, allow_insecure)
293308 self .models = nil
294309 self .claude_enabled = false
295310 self .current_job = nil
311+ self .extra_args = {
312+ -- Retry failed requests twice
313+ ' --retry' ,
314+ ' 2' ,
315+ -- Wait 1 second between retries
316+ ' --retry-delay' ,
317+ ' 1' ,
318+ -- Maximum time for the request
319+ ' --max-time' ,
320+ math.floor (timeout * 2 / 1000 ),
321+ -- Timeout for initial connection
322+ ' --connect-timeout' ,
323+ ' 10' ,
324+ ' --no-keepalive' , -- Don't reuse connections
325+ ' --tcp-nodelay' , -- Disable Nagle's algorithm for faster streaming
326+ ' --no-buffer' , -- Disable output buffering for streaming
327+ ' --fail' , -- Return error on HTTP errors (4xx, 5xx)
328+ ' --silent' , -- Don't show progress meter
329+ ' --show-error' , -- Show errors even when silent
330+ }
296331end )
297332
298333function Copilot :authenticate ()
@@ -314,13 +349,18 @@ function Copilot:authenticate()
314349 headers [key ] = value
315350 end
316351
317- local response = curl_get (' https://api.github.com/copilot_internal/v2/token' , {
352+ local response , err = curl_get (' https://api.github.com/copilot_internal/v2/token' , {
318353 timeout = timeout ,
319354 headers = headers ,
320355 proxy = self .proxy ,
321356 insecure = self .allow_insecure ,
357+ raw = self .extra_args ,
322358 })
323359
360+ if err then
361+ error (err )
362+ end
363+
324364 if response .status ~= 200 then
325365 error (' Failed to authenticate: ' .. tostring (response .status ))
326366 end
@@ -351,13 +391,18 @@ function Copilot:fetch_models()
351391 return self .models
352392 end
353393
354- local response = curl_get (' https://api.githubcopilot.com/models' , {
394+ local response , err = curl_get (' https://api.githubcopilot.com/models' , {
355395 timeout = timeout ,
356396 headers = self :authenticate (),
357397 proxy = self .proxy ,
358398 insecure = self .allow_insecure ,
399+ raw = self .extra_args ,
359400 })
360401
402+ if err then
403+ error (err )
404+ end
405+
361406 if response .status ~= 200 then
362407 error (' Failed to fetch models: ' .. tostring (response .status ))
363408 end
@@ -385,14 +430,19 @@ function Copilot:enable_claude()
385430 local business_msg =
386431 ' Claude is probably enabled (for business users needs to be enabled manually).'
387432
388- local response = curl_post (' https://api.githubcopilot.com/models/claude-3.5-sonnet/policy' , {
433+ local response , err = curl_post (' https://api.githubcopilot.com/models/claude-3.5-sonnet/policy' , {
389434 timeout = timeout ,
390435 headers = self :authenticate (),
391436 proxy = self .proxy ,
392437 insecure = self .allow_insecure ,
393438 body = temp_file (' {"state": "enabled"}' ),
439+ raw = self .extra_args ,
394440 })
395441
442+ if err then
443+ error (err )
444+ end
445+
396446 -- Handle business user case
397447 if response .status ~= 200 and string.find (tostring (response .body ), business_check ) then
398448 self .claude_enabled = true
@@ -494,29 +544,44 @@ function Copilot:ask(prompt, opts)
494544
495545 local last_message = nil
496546 local errored = false
547+ local finished = false
497548 local full_response = ' '
498549
499- local function handle_error (err )
500- errored = true
501- full_response = err
550+ local function finish_stream (err , job )
551+ if err then
552+ errored = true
553+ full_response = err
554+ end
555+
556+ finished = true
557+ vim .schedule (function ()
558+ job :shutdown ()
559+ end )
502560 end
503561
504- local function stream_func (err , line )
505- if not line or errored then
562+ local function stream_func (err , line , job )
563+ if not line or errored or finished then
506564 return
507565 end
508566
509567 if self .current_job ~= job_id then
568+ finish_stream (nil , job )
510569 return
511570 end
512571
513572 if err or vim .startswith (line , ' {"error"' ) then
514- handle_error (' Failed to get response: ' .. (err and vim .inspect (err ) or line ))
573+ finish_stream (' Failed to get response: ' .. (err and vim .inspect (err ) or line ), job )
574+ return
575+ end
576+
577+ if not vim .startswith (line , ' data: ' ) then
515578 return
516579 end
517580
518- line = line :gsub (' ^%s*data: ' , ' ' )
519- if line == ' ' or line == ' [DONE]' then
581+ line = line :gsub (' ^%s*data:%s*' , ' ' ):gsub (' %s*$' , ' ' )
582+
583+ if line == ' [DONE]' then
584+ finish_stream (nil , job )
520585 return
521586 end
522587
@@ -528,7 +593,7 @@ function Copilot:ask(prompt, opts)
528593 })
529594
530595 if not ok then
531- handle_error (' Failed to parse response: ' .. vim .inspect (content ) .. ' \n ' .. line )
596+ finish_stream (' Failed to parse response: ' .. vim .inspect (content ) .. ' \n ' .. line , job )
532597 return
533598 end
534599
@@ -538,8 +603,7 @@ function Copilot:ask(prompt, opts)
538603
539604 last_message = content
540605 local choice = content .choices [1 ]
541- local is_full = choice .message ~= nil
542- content = is_full and choice .message .content or choice .delta .content
606+ content = choice .message and choice .message .content or choice .delta and choice .delta .content
543607
544608 if not content then
545609 return
@@ -569,13 +633,14 @@ function Copilot:ask(prompt, opts)
569633 self :enable_claude ()
570634 end
571635
572- local response = curl_post (' https://api.githubcopilot.com/chat/completions' , {
636+ local response , err = curl_post (' https://api.githubcopilot.com/chat/completions' , {
573637 timeout = timeout ,
574638 headers = self :authenticate (),
575639 body = temp_file (body ),
576640 proxy = self .proxy ,
577641 insecure = self .allow_insecure ,
578642 stream = stream_func ,
643+ raw = self .extra_args ,
579644 })
580645
581646 if self .current_job ~= job_id then
@@ -584,6 +649,11 @@ function Copilot:ask(prompt, opts)
584649
585650 self .current_job = nil
586651
652+ if err then
653+ error (err )
654+ return
655+ end
656+
587657 if not response then
588658 error (' Failed to get response' )
589659 return
@@ -662,14 +732,20 @@ function Copilot:embed(inputs, opts)
662732 local out = {}
663733
664734 for _ , chunk in ipairs (chunks ) do
665- local response = curl_post (' https://api.githubcopilot.com/embeddings' , {
735+ local response , err = curl_post (' https://api.githubcopilot.com/embeddings' , {
666736 timeout = timeout ,
667737 headers = self :authenticate (),
668738 body = temp_file (vim .json .encode (generate_embedding_request (chunk , model ))),
669739 proxy = self .proxy ,
670740 insecure = self .allow_insecure ,
741+ raw = self .extra_args ,
671742 })
672743
744+ if err then
745+ error (err )
746+ return
747+ end
748+
673749 if not response then
674750 error (' Failed to get response' )
675751 return
0 commit comments