1
1
import { Configuration , CreateCompletionRequestPrompt , CreateCompletionResponse , OpenAIApi } from 'openai' ;
2
2
import { CancellationToken , InlineCompletionContext , InlineCompletionItem , InlineCompletionItemProvider , InlineCompletionList , Position , ProviderResult , Range , TextDocument , workspace } from 'vscode' ;
3
3
import { AxiosResponse } from 'axios' ;
4
- import { nextId } from './Uuid ' ;
4
+ import { debounce } from './utilities ' ;
5
5
6
6
export class FauxpilotCompletionProvider implements InlineCompletionItemProvider {
7
- cachedPrompts : Map < string , number > = new Map < string , number > ( ) ;
8
-
7
+ // TODO: Make dynamic
8
+ // AFAIK VSCode creates provider once. As such, token will never be updated
9
9
private configuration : Configuration = new Configuration ( {
10
10
apiKey : workspace . getConfiguration ( 'fauxpilot' ) . get ( "token" )
11
11
} ) ;
12
+ // TODO: Make dynamic
13
+ // AFAIK VSCode creates provider once. As such, server address will never be updated
12
14
private openai : OpenAIApi = new OpenAIApi ( this . configuration , `${ workspace . getConfiguration ( 'fauxpilot' ) . get ( "server" ) } /${ workspace . getConfiguration ( 'fauxpilot' ) . get ( "engine" ) } ` ) ;
13
- private request_status : string = "done" ;
15
+ private readonly debouncedApiCall : any = debounce (
16
+ // TODO: Extract to method.
17
+ // I absolutely forgot how to handle 'this' context in JS. Simple extraction makes this
18
+ // undefined. How to bind it?
19
+ ( prompt : string , position : Position ) => {
20
+ return new Promise ( resolve => {
21
+ console . debug ( "Requesting completion after debounce period" ) ;
22
+ this . callOpenAi ( prompt ) . then ( ( response ) => {
23
+ resolve ( this . toInlineCompletions ( response . data , position ) ) ;
24
+ } ) . catch ( ( error ) => {
25
+ console . error ( error ) ;
26
+ resolve ( ( [ ] as InlineCompletionItem [ ] ) ) ;
27
+ } ) ;
28
+ } ) ;
29
+ } , { timeout : workspace . getConfiguration ( 'fauxpilot' ) . get ( "suggestionDelay" ) as number , defaultReturn : [ ] } ) ;
30
+
31
+ constructor ( private testCompletion ?: any ) {
32
+ }
14
33
15
34
//@ts -ignore
16
35
// because ASYNC and PROMISE
@@ -21,43 +40,14 @@ export class FauxpilotCompletionProvider implements InlineCompletionItemProvider
21
40
}
22
41
23
42
const prompt = this . getPrompt ( document , position ) ;
24
- console . debug ( "Requesting completion for prompt" , prompt ) ;
25
43
26
44
if ( this . isNil ( prompt ) ) {
27
45
console . debug ( "Prompt is empty, skipping" ) ;
28
46
return Promise . resolve ( ( [ ] as InlineCompletionItem [ ] ) ) ;
29
47
}
30
48
31
- const currentTimestamp = Date . now ( ) ;
32
- const currentId = nextId ( ) ;
33
- this . cachedPrompts . set ( currentId , currentTimestamp ) ;
34
-
35
- // check there is no newer request util this.request_status is done
36
- while ( this . request_status === "pending" ) {
37
- await this . sleep ( 200 ) ;
38
- console . debug ( "current id = " , currentId , " request status = " , this . request_status ) ;
39
- if ( this . newestTimestamp ( ) > currentTimestamp ) {
40
- console . debug ( "newest timestamp=" , this . newestTimestamp ( ) , "current timestamp=" , currentTimestamp ) ;
41
- console . debug ( "Newer request is pending, skipping" ) ;
42
- this . cachedPrompts . delete ( currentId ) ;
43
- return Promise . resolve ( ( [ ] as InlineCompletionItem [ ] ) ) ;
44
- }
45
- }
46
-
47
- console . debug ( "current id = " , currentId , "set request status to pending" ) ;
48
- this . request_status = "pending" ;
49
- return this . callOpenAi ( prompt as String ) . then ( ( response ) => {
50
- console . debug ( "current id = " , currentId , "set request status to done" ) ;
51
- this . request_status = "done" ;
52
- this . cachedPrompts . delete ( currentId ) ;
53
- return this . toInlineCompletions ( response . data , position ) ;
54
- } ) . catch ( ( error ) => {
55
- console . debug ( "current id = " , currentId , "set request status to done" ) ;
56
- this . request_status = "done" ;
57
- this . cachedPrompts . delete ( currentId ) ;
58
- console . error ( error ) ;
59
- return ( [ ] as InlineCompletionItem [ ] ) ;
60
- } ) ;
49
+ console . debug ( "Requesting completion for prompt" , prompt ) ;
50
+ return this . debouncedApiCall ( prompt , position ) ;
61
51
}
62
52
63
53
private getPrompt ( document : TextDocument , position : Position ) : String | undefined {
@@ -72,27 +62,22 @@ export class FauxpilotCompletionProvider implements InlineCompletionItemProvider
72
62
return value === undefined || value === null || value . length === 0 ;
73
63
}
74
64
75
- private newestTimestamp ( ) {
76
- return Array . from ( this . cachedPrompts . values ( ) ) . reduce ( ( a , b ) => Math . max ( a , b ) ) ;
77
- }
78
-
79
- private sleep ( milliseconds : number ) {
80
- return new Promise ( r => setTimeout ( r , milliseconds ) ) ;
81
- } ;
82
-
83
65
private callOpenAi ( prompt : String ) : Promise < AxiosResponse < CreateCompletionResponse , any > > {
84
66
console . debug ( "Calling OpenAi" , prompt ) ;
85
67
68
+ // FIXME: I do not understand my own comment below. To verify
86
69
//check if inline completion is enabled
87
- const stop_words = workspace . getConfiguration ( 'fauxpilot' ) . get ( "inlineCompletion" ) ? [ "\n" ] : [ ] ;
88
- console . debug ( "Calling OpenAi with stop words = " , stop_words ) ;
89
- return this . openai . createCompletion ( {
70
+ const stopWords = workspace . getConfiguration ( 'fauxpilot' ) . get ( "inlineCompletion" ) ? [ "\n" ] : [ ] ;
71
+ console . debug ( "Calling OpenAi with stop words = " , stopWords ) ;
72
+ // FIXME: how to mock in mocha?
73
+ // Current implementation works by injecting alternative provider via constructor
74
+ return ( this . testCompletion ?? this . openai ) . createCompletion ( {
90
75
model : workspace . getConfiguration ( 'fauxpilot' ) . get ( "model" ) ?? "<<UNSET>>" ,
91
76
prompt : prompt as CreateCompletionRequestPrompt ,
92
77
/* eslint-disable-next-line @typescript-eslint/naming-convention */
93
78
max_tokens : workspace . getConfiguration ( 'fauxpilot' ) . get ( "maxTokens" ) ,
94
79
temperature : workspace . getConfiguration ( 'fauxpilot' ) . get ( "temperature" ) ,
95
- stop : stop_words
80
+ stop : stopWords
96
81
} ) ;
97
82
}
98
83
0 commit comments