@@ -30,6 +30,8 @@ namespace Microsoft.Azure.PowerShell.Tools.AzPredictor
30
30
/// </summary>
31
31
internal class AzPredictorService : IAzPredictorService , IDisposable
32
32
{
33
+ private const string ClientType = "AzurePowerShell" ;
34
+
33
35
[ JsonObject ( NamingStrategyType = typeof ( CamelCaseNamingStrategy ) ) ]
34
36
private sealed class PredictionRequestBody
35
37
{
@@ -38,16 +40,22 @@ public sealed class RequestContext
38
40
public string CorrelationId { get ; set ; } = Guid . Empty . ToString ( ) ;
39
41
public string SessionId { get ; set ; } = Guid . Empty . ToString ( ) ;
40
42
public string SubscriptionId { get ; set ; } = Guid . Empty . ToString ( ) ;
41
- public Version VersionNumber { get ; set ; } = new Version ( 1 , 0 ) ;
43
+ public Version VersionNumber { get ; set ; } = new Version ( 0 , 0 ) ;
42
44
}
43
45
44
46
public string History { get ; set ; }
45
- public string ClientType { get ; set ; } = "AzurePowerShell" ;
47
+ public string ClientType { get ; set ; } = AzPredictorService . ClientType ;
46
48
public RequestContext Context { get ; set ; } = new RequestContext ( ) ;
47
49
48
50
public PredictionRequestBody ( string command ) => this . History = command ;
49
51
} ;
50
52
53
+ [ JsonObject ( NamingStrategyType = typeof ( CamelCaseNamingStrategy ) ) ]
54
+ private sealed class CommandRequestContext
55
+ {
56
+ public Version VersionNumber { get ; set ; } = new Version ( 0 , 0 ) ;
57
+ }
58
+
51
59
private static readonly HttpClient _client = new HttpClient ( ) ;
52
60
private readonly string _commandsEndpoint ;
53
61
private readonly string _predictionsEndpoint ;
@@ -56,7 +64,7 @@ public sealed class RequestContext
56
64
private volatile string _commandForPrediction ;
57
65
private HashSet < string > _commandSet ;
58
66
private CancellationTokenSource _predictionRequestCancellationSource ;
59
- private ParameterValuePredictor _parameterValuePredictor = new ParameterValuePredictor ( ) ;
67
+ private readonly ParameterValuePredictor _parameterValuePredictor = new ParameterValuePredictor ( ) ;
60
68
61
69
private readonly ITelemetryClient _telemetryClient ;
62
70
@@ -68,7 +76,7 @@ public sealed class RequestContext
68
76
/// <param name="telemetryClient">The telemetry client.</param>
69
77
public AzPredictorService ( string serviceUri , ITelemetryClient telemetryClient )
70
78
{
71
- this . _commandsEndpoint = serviceUri + AzPredictorConstants . CommandsEndpoint ;
79
+ this . _commandsEndpoint = $ " { serviceUri } { AzPredictorConstants . CommandsEndpoint } ?clientType= { AzPredictorService . ClientType } &context= { JsonConvert . SerializeObject ( new CommandRequestContext ( ) ) } " ;
72
80
this . _predictionsEndpoint = serviceUri + AzPredictorConstants . PredictionsEndpoint ;
73
81
this . _telemetryClient = telemetryClient ;
74
82
@@ -109,16 +117,12 @@ protected virtual void Dispose(bool disposing)
109
117
/// <remarks>
110
118
/// Queries the Predictor with the user input if predictions are available, otherwise uses commands
111
119
/// </remarks>
112
- public IEnumerable < ValueTuple < string , PredictionSource > > GetSuggestion ( Ast input , int suggestionCount , CancellationToken cancellationToken )
120
+ public IEnumerable < ValueTuple < string , IList < Tuple < string , string > > , PredictionSource > > GetSuggestion ( Ast input , int suggestionCount , CancellationToken cancellationToken )
113
121
{
114
122
var commandSuggestions = this . _commandSuggestions ;
115
123
var command = this . _commandForPrediction ;
116
124
117
- // We've already used _commandSuggestions. There is no need to wait the request to complete at this point.
118
- // Cancel it.
119
- this . _predictionRequestCancellationSource ? . Cancel ( ) ;
120
-
121
- IList < ValueTuple < string , PredictionSource > > results = new List < ValueTuple < string , PredictionSource > > ( ) ;
125
+ IList < ValueTuple < string , IList < Tuple < string , string > > , PredictionSource > > results = new List < ValueTuple < string , IList < Tuple < string , string > > , PredictionSource > > ( ) ;
122
126
123
127
var resultsFromSuggestion = commandSuggestions ? . Item2 ? . Query ( input , suggestionCount , cancellationToken ) ;
124
128
@@ -135,9 +139,12 @@ public IEnumerable<ValueTuple<string, PredictionSource>> GetSuggestion(Ast input
135
139
predictionSource = PredictionSource . PreviousCommand ;
136
140
}
137
141
138
- foreach ( var r in resultsFromSuggestion )
142
+ if ( resultsFromSuggestion != null )
139
143
{
140
- results . Add ( ValueTuple . Create ( r , predictionSource ) ) ;
144
+ foreach ( var r in resultsFromSuggestion )
145
+ {
146
+ results . Add ( ValueTuple . Create ( r . Key , r . Value , predictionSource ) ) ;
147
+ }
141
148
}
142
149
}
143
150
@@ -146,13 +153,16 @@ public IEnumerable<ValueTuple<string, PredictionSource>> GetSuggestion(Ast input
146
153
var commands = this . _commands ;
147
154
var resultsFromCommands = commands ? . Query ( input , suggestionCount - resultsFromSuggestion . Count ( ) , cancellationToken ) ;
148
155
149
- resultsFromCommands ? . ExceptWith ( resultsFromSuggestion ) ;
150
-
151
156
if ( resultsFromCommands != null )
152
157
{
153
158
foreach ( var r in resultsFromCommands )
154
159
{
155
- results . Add ( ValueTuple . Create ( r , PredictionSource . StaticCommands ) ) ;
160
+ if ( resultsFromCommands ? . ContainsKey ( r . Key ) == true )
161
+ {
162
+ continue ;
163
+ }
164
+
165
+ results . Add ( ValueTuple . Create ( r . Key , r . Value , PredictionSource . StaticCommands ) ) ;
156
166
}
157
167
}
158
168
}
@@ -163,45 +173,54 @@ public IEnumerable<ValueTuple<string, PredictionSource>> GetSuggestion(Ast input
163
173
/// <inheritdoc/>
164
174
public virtual void RequestPredictions ( IEnumerable < string > commands )
165
175
{
166
- // Even if it's called multiple times, we only need to keep the one for the latest command.
167
-
168
- this . _predictionRequestCancellationSource ? . Cancel ( ) ;
169
- this . _predictionRequestCancellationSource = new CancellationTokenSource ( ) ;
170
-
171
- var cancellationToken = this . _predictionRequestCancellationSource . Token ;
172
-
173
176
var localCommands = string . Join ( AzPredictorConstants . CommandConcatenator , commands ) ;
174
177
this . _telemetryClient . OnRequestPrediction ( localCommands ) ;
175
- this . SetPredictionCommand ( localCommands ) ;
176
178
177
- // We don't need to block on the task. We send the HTTP request and update prediction list at the background.
178
- Task . Run ( async ( ) => {
179
- try
180
- {
181
- var requestContext = new PredictionRequestBody . RequestContext ( )
182
- {
183
- SessionId = this . _telemetryClient . SessionId ,
184
- CorrelationId = this . _telemetryClient . CorrelationId ,
185
- } ;
186
- var requestBody = new PredictionRequestBody ( localCommands )
187
- {
188
- Context = requestContext ,
189
- } ;
179
+ if ( string . Equals ( localCommands , this . _commandForPrediction , StringComparison . Ordinal ) )
180
+ {
181
+ // It's the same history we've already requested the prediction for last time, skip it.
182
+ return ;
183
+ }
184
+ else
185
+ {
186
+ this . SetPredictionCommand ( localCommands ) ;
190
187
191
- var requestBodyString = JsonConvert . SerializeObject ( requestBody ) ;
192
- var httpResponseMessage = await _client . PostAsync ( this . _predictionsEndpoint , new StringContent ( requestBodyString , Encoding . UTF8 , "application/json" ) , cancellationToken ) ;
188
+ // When it's called multiple times, we only need to keep the one for the latest command.
193
189
194
- var reply = await httpResponseMessage . Content . ReadAsStringAsync ( cancellationToken ) ;
195
- var suggestionsList = JsonConvert . DeserializeObject < List < string > > ( reply ) ;
190
+ this . _predictionRequestCancellationSource ? . Cancel ( ) ;
191
+ this . _predictionRequestCancellationSource = new CancellationTokenSource ( ) ;
196
192
197
- this . SetSuggestionPredictor ( localCommands , suggestionsList ) ;
198
- }
199
- catch ( Exception e ) when ( ! ( e is OperationCanceledException ) )
200
- {
201
- this . _telemetryClient . OnRequestPredictionError ( localCommands , e ) ;
202
- }
203
- } ,
204
- cancellationToken ) ;
193
+ var cancellationToken = this . _predictionRequestCancellationSource . Token ;
194
+
195
+ // We don't need to block on the task. We send the HTTP request and update prediction list at the background.
196
+ Task . Run ( async ( ) => {
197
+ try
198
+ {
199
+ var requestContext = new PredictionRequestBody . RequestContext ( )
200
+ {
201
+ SessionId = this . _telemetryClient . SessionId ,
202
+ CorrelationId = this . _telemetryClient . CorrelationId ,
203
+ } ;
204
+ var requestBody = new PredictionRequestBody ( localCommands )
205
+ {
206
+ Context = requestContext ,
207
+ } ;
208
+
209
+ var requestBodyString = JsonConvert . SerializeObject ( requestBody ) ;
210
+ var httpResponseMessage = await _client . PostAsync ( this . _predictionsEndpoint , new StringContent ( requestBodyString , Encoding . UTF8 , "application/json" ) , cancellationToken ) ;
211
+
212
+ var reply = await httpResponseMessage . Content . ReadAsStringAsync ( cancellationToken ) ;
213
+ var suggestionsList = JsonConvert . DeserializeObject < List < string > > ( reply ) ;
214
+
215
+ this . SetSuggestionPredictor ( localCommands , suggestionsList ) ;
216
+ }
217
+ catch ( Exception e ) when ( ! ( e is OperationCanceledException ) )
218
+ {
219
+ this . _telemetryClient . OnRequestPredictionError ( localCommands , e ) ;
220
+ }
221
+ } ,
222
+ cancellationToken ) ;
223
+ }
205
224
}
206
225
207
226
/// <inheritdoc/>
0 commit comments