@vscode/chat-lib 0.4.1-9 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/dist/src/_internal/extension/byok/node/openAIEndpoint.d.ts +2 -1
  2. package/dist/src/_internal/extension/byok/node/openAIEndpoint.d.ts.map +1 -1
  3. package/dist/src/_internal/extension/byok/node/openAIEndpoint.js +6 -7
  4. package/dist/src/_internal/extension/byok/node/openAIEndpoint.js.map +1 -1
  5. package/dist/src/_internal/extension/common/constants.d.ts +0 -1
  6. package/dist/src/_internal/extension/common/constants.d.ts.map +1 -1
  7. package/dist/src/_internal/extension/common/constants.js.map +1 -1
  8. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.d.ts +9 -0
  9. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.d.ts.map +1 -1
  10. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.js +71 -11
  11. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.js.map +1 -1
  12. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.d.ts +17 -1
  13. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.d.ts.map +1 -1
  14. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.js +345 -57
  15. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.js.map +1 -1
  16. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.d.ts +6 -3
  17. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.d.ts.map +1 -1
  18. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.js +9 -3
  19. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.js.map +1 -1
  20. package/dist/src/_internal/extension/xtab/node/xtabEndpoint.d.ts +2 -1
  21. package/dist/src/_internal/extension/xtab/node/xtabEndpoint.d.ts.map +1 -1
  22. package/dist/src/_internal/extension/xtab/node/xtabEndpoint.js +6 -3
  23. package/dist/src/_internal/extension/xtab/node/xtabEndpoint.js.map +1 -1
  24. package/dist/src/_internal/extension/xtab/node/xtabNextCursorPredictor.d.ts.map +1 -1
  25. package/dist/src/_internal/extension/xtab/node/xtabNextCursorPredictor.js +1 -0
  26. package/dist/src/_internal/extension/xtab/node/xtabNextCursorPredictor.js.map +1 -1
  27. package/dist/src/_internal/extension/xtab/node/xtabProvider.d.ts +2 -0
  28. package/dist/src/_internal/extension/xtab/node/xtabProvider.d.ts.map +1 -1
  29. package/dist/src/_internal/extension/xtab/node/xtabProvider.js +14 -0
  30. package/dist/src/_internal/extension/xtab/node/xtabProvider.js.map +1 -1
  31. package/dist/src/_internal/platform/chat/common/commonTypes.d.ts +2 -1
  32. package/dist/src/_internal/platform/chat/common/commonTypes.d.ts.map +1 -1
  33. package/dist/src/_internal/platform/chat/common/commonTypes.js +61 -17
  34. package/dist/src/_internal/platform/chat/common/commonTypes.js.map +1 -1
  35. package/dist/src/_internal/platform/configuration/common/configurationService.d.ts +14 -2
  36. package/dist/src/_internal/platform/configuration/common/configurationService.d.ts.map +1 -1
  37. package/dist/src/_internal/platform/configuration/common/configurationService.js +29 -3
  38. package/dist/src/_internal/platform/configuration/common/configurationService.js.map +1 -1
  39. package/dist/src/_internal/platform/endpoint/common/capiClient.d.ts.map +1 -1
  40. package/dist/src/_internal/platform/endpoint/common/capiClient.js +1 -0
  41. package/dist/src/_internal/platform/endpoint/common/capiClient.js.map +1 -1
  42. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.d.ts +1 -0
  43. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.d.ts.map +1 -1
  44. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.js +20 -4
  45. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.js.map +1 -1
  46. package/dist/src/_internal/platform/endpoint/common/endpointProvider.d.ts +3 -1
  47. package/dist/src/_internal/platform/endpoint/common/endpointProvider.d.ts.map +1 -1
  48. package/dist/src/_internal/platform/endpoint/common/endpointProvider.js +1 -0
  49. package/dist/src/_internal/platform/endpoint/common/endpointProvider.js.map +1 -1
  50. package/dist/src/_internal/platform/endpoint/node/autoChatEndpoint.d.ts +2 -1
  51. package/dist/src/_internal/platform/endpoint/node/autoChatEndpoint.d.ts.map +1 -1
  52. package/dist/src/_internal/platform/endpoint/node/autoChatEndpoint.js +6 -3
  53. package/dist/src/_internal/platform/endpoint/node/autoChatEndpoint.js.map +1 -1
  54. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.d.ts +6 -2
  55. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.d.ts.map +1 -1
  56. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.js +34 -12
  57. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.js.map +1 -1
  58. package/dist/src/_internal/platform/endpoint/node/copilotChatEndpoint.d.ts +2 -1
  59. package/dist/src/_internal/platform/endpoint/node/copilotChatEndpoint.d.ts.map +1 -1
  60. package/dist/src/_internal/platform/endpoint/node/copilotChatEndpoint.js +5 -3
  61. package/dist/src/_internal/platform/endpoint/node/copilotChatEndpoint.js.map +1 -1
  62. package/dist/src/_internal/platform/endpoint/node/messagesApi.d.ts.map +1 -1
  63. package/dist/src/_internal/platform/endpoint/node/messagesApi.js +55 -5
  64. package/dist/src/_internal/platform/endpoint/node/messagesApi.js.map +1 -1
  65. package/dist/src/_internal/platform/endpoint/node/proxyXtabEndpoint.d.ts.map +1 -1
  66. package/dist/src/_internal/platform/endpoint/node/proxyXtabEndpoint.js +1 -0
  67. package/dist/src/_internal/platform/endpoint/node/proxyXtabEndpoint.js.map +1 -1
  68. package/dist/src/_internal/platform/endpoint/node/responsesApi.d.ts +1 -0
  69. package/dist/src/_internal/platform/endpoint/node/responsesApi.d.ts.map +1 -1
  70. package/dist/src/_internal/platform/endpoint/node/responsesApi.js +14 -10
  71. package/dist/src/_internal/platform/endpoint/node/responsesApi.js.map +1 -1
  72. package/dist/src/_internal/platform/git/common/gitService.d.ts +4 -0
  73. package/dist/src/_internal/platform/git/common/gitService.d.ts.map +1 -1
  74. package/dist/src/_internal/platform/git/common/gitService.js.map +1 -1
  75. package/dist/src/_internal/platform/github/common/githubAPI.d.ts +1 -0
  76. package/dist/src/_internal/platform/github/common/githubAPI.d.ts.map +1 -1
  77. package/dist/src/_internal/platform/github/common/githubAPI.js +2 -0
  78. package/dist/src/_internal/platform/github/common/githubAPI.js.map +1 -1
  79. package/dist/src/_internal/platform/github/common/githubService.d.ts +10 -0
  80. package/dist/src/_internal/platform/github/common/githubService.d.ts.map +1 -1
  81. package/dist/src/_internal/platform/github/common/githubService.js +38 -0
  82. package/dist/src/_internal/platform/github/common/githubService.js.map +1 -1
  83. package/dist/src/_internal/platform/github/common/nullOctokitServiceImpl.d.ts +2 -1
  84. package/dist/src/_internal/platform/github/common/nullOctokitServiceImpl.d.ts.map +1 -1
  85. package/dist/src/_internal/platform/github/common/nullOctokitServiceImpl.js +3 -0
  86. package/dist/src/_internal/platform/github/common/nullOctokitServiceImpl.js.map +1 -1
  87. package/dist/src/_internal/platform/inlineEdits/common/dataTypes/xtabPromptOptions.d.ts +1 -1
  88. package/dist/src/_internal/platform/inlineEdits/common/dataTypes/xtabPromptOptions.d.ts.map +1 -1
  89. package/dist/src/_internal/platform/inlineEdits/common/dataTypes/xtabPromptOptions.js +1 -1
  90. package/dist/src/_internal/platform/inlineEdits/common/dataTypes/xtabPromptOptions.js.map +1 -1
  91. package/dist/src/_internal/platform/networking/common/anthropic.d.ts +8 -1
  92. package/dist/src/_internal/platform/networking/common/anthropic.d.ts.map +1 -1
  93. package/dist/src/_internal/platform/networking/common/anthropic.js +20 -4
  94. package/dist/src/_internal/platform/networking/common/anthropic.js.map +1 -1
  95. package/dist/src/_internal/platform/networking/common/fetcherService.d.ts +17 -0
  96. package/dist/src/_internal/platform/networking/common/fetcherService.d.ts.map +1 -1
  97. package/dist/src/_internal/platform/networking/common/fetcherService.js +27 -1
  98. package/dist/src/_internal/platform/networking/common/fetcherService.js.map +1 -1
  99. package/dist/src/_internal/platform/networking/common/networking.d.ts +7 -0
  100. package/dist/src/_internal/platform/networking/common/networking.d.ts.map +1 -1
  101. package/dist/src/_internal/platform/networking/common/networking.js +4 -5
  102. package/dist/src/_internal/platform/networking/common/networking.js.map +1 -1
  103. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.d.ts +76 -0
  104. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.d.ts.map +1 -0
  105. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.js +480 -0
  106. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.js.map +1 -0
  107. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.d.ts +87 -0
  108. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.d.ts.map +1 -0
  109. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.js +265 -0
  110. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.js.map +1 -0
  111. package/dist/src/_internal/platform/otel/common/genAiAttributes.d.ts +85 -0
  112. package/dist/src/_internal/platform/otel/common/genAiAttributes.d.ts.map +1 -0
  113. package/dist/src/_internal/platform/otel/common/genAiAttributes.js +105 -0
  114. package/dist/src/_internal/platform/otel/common/genAiAttributes.js.map +1 -0
  115. package/dist/src/_internal/platform/otel/common/genAiEvents.d.ts +28 -0
  116. package/dist/src/_internal/platform/otel/common/genAiEvents.d.ts.map +1 -0
  117. package/dist/src/_internal/platform/otel/common/genAiEvents.js +91 -0
  118. package/dist/src/_internal/platform/otel/common/genAiEvents.js.map +1 -0
  119. package/dist/src/_internal/platform/otel/common/genAiMetrics.d.ts +30 -0
  120. package/dist/src/_internal/platform/otel/common/genAiMetrics.d.ts.map +1 -0
  121. package/dist/src/_internal/platform/otel/common/genAiMetrics.js +68 -0
  122. package/dist/src/_internal/platform/otel/common/genAiMetrics.js.map +1 -0
  123. package/dist/src/_internal/platform/otel/common/index.d.ts +8 -0
  124. package/dist/src/_internal/platform/otel/common/index.d.ts.map +1 -0
  125. package/dist/src/_internal/platform/otel/common/index.js +35 -0
  126. package/dist/src/_internal/platform/otel/common/index.js.map +1 -0
  127. package/dist/src/_internal/platform/otel/common/messageFormatters.d.ts +85 -0
  128. package/dist/src/_internal/platform/otel/common/messageFormatters.d.ts.map +1 -0
  129. package/dist/src/_internal/platform/otel/common/messageFormatters.js +122 -0
  130. package/dist/src/_internal/platform/otel/common/messageFormatters.js.map +1 -0
  131. package/dist/src/_internal/platform/otel/common/noopOtelService.d.ts +23 -0
  132. package/dist/src/_internal/platform/otel/common/noopOtelService.d.ts.map +1 -0
  133. package/dist/src/_internal/platform/otel/common/noopOtelService.js +46 -0
  134. package/dist/src/_internal/platform/otel/common/noopOtelService.js.map +1 -0
  135. package/dist/src/_internal/platform/otel/common/otelConfig.d.ts +35 -0
  136. package/dist/src/_internal/platform/otel/common/otelConfig.d.ts.map +1 -0
  137. package/dist/src/_internal/platform/otel/common/otelConfig.js +140 -0
  138. package/dist/src/_internal/platform/otel/common/otelConfig.js.map +1 -0
  139. package/dist/src/_internal/platform/otel/common/otelService.d.ts +100 -0
  140. package/dist/src/_internal/platform/otel/common/otelService.d.ts.map +1 -0
  141. package/dist/src/_internal/platform/otel/common/otelService.js +10 -0
  142. package/dist/src/_internal/platform/otel/common/otelService.js.map +1 -0
  143. package/dist/src/main.d.ts.map +1 -1
  144. package/dist/src/main.js +9 -0
  145. package/dist/src/main.js.map +1 -1
  146. package/dist/src/package.json +226 -80
  147. package/package.json +3 -2
@@ -12,6 +12,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
12
12
  var __param = (this && this.__param) || function (paramIndex, decorator) {
13
13
  return function (target, key) { decorator(target, key, paramIndex); }
14
14
  };
15
+ var ChatMLFetcherImpl_1;
15
16
  Object.defineProperty(exports, "__esModule", { value: true });
16
17
  exports.ChatMLFetcherImpl = exports.AbstractChatMLFetcher = void 0;
17
18
  exports.createTelemetryData = createTelemetryData;
@@ -35,8 +36,11 @@ const fetcherService_1 = require("../../../platform/networking/common/fetcherSer
35
36
  const networking_1 = require("../../../platform/networking/common/networking");
36
37
  const openai_1 = require("../../../platform/networking/common/openai");
37
38
  const chatStream_1 = require("../../../platform/networking/node/chatStream");
39
+ const chatWebSocketManager_1 = require("../../../platform/networking/node/chatWebSocketManager");
38
40
  const stream_1 = require("../../../platform/networking/node/stream");
39
41
  const fetch_2 = require("../../../platform/openai/node/fetch");
42
+ const index_1 = require("../../../platform/otel/common/index");
43
+ const otelService_1 = require("../../../platform/otel/common/otelService");
40
44
  const requestLogger_1 = require("../../../platform/requestLogger/node/requestLogger");
41
45
  const nullExperimentationService_1 = require("../../../platform/telemetry/common/nullExperimentationService");
42
46
  const telemetry_1 = require("../../../platform/telemetry/common/telemetry");
@@ -83,7 +87,9 @@ class AbstractChatMLFetcher extends lifecycle_1.Disposable {
83
87
  }
84
88
  exports.AbstractChatMLFetcher = AbstractChatMLFetcher;
85
89
  let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
86
- constructor(_fetcherService, _telemetryService, _requestLogger, _logService, _authenticationService, _interactionService, _chatQuotaService, _capiClientService, options, _configurationService, _experimentationService, _powerService, _instantiationService) {
90
+ static { ChatMLFetcherImpl_1 = this; }
91
+ static { this._maxConsecutiveWebSocketFallbacks = 3; }
92
+ constructor(_fetcherService, _telemetryService, _requestLogger, _logService, _authenticationService, _interactionService, _chatQuotaService, _capiClientService, options, _configurationService, _experimentationService, _powerService, _instantiationService, _webSocketManager, _otelService) {
87
93
  super(options);
88
94
  this._fetcherService = _fetcherService;
89
95
  this._telemetryService = _telemetryService;
@@ -97,23 +103,36 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
97
103
  this._experimentationService = _experimentationService;
98
104
  this._powerService = _powerService;
99
105
  this._instantiationService = _instantiationService;
106
+ this._webSocketManager = _webSocketManager;
107
+ this._otelService = _otelService;
100
108
  /**
101
109
  * Delays (in ms) between connectivity check attempts before retrying a failed request.
102
110
  * Configurable for testing purposes.
103
111
  */
104
112
  this.connectivityCheckDelays = [1000, 10000, 10000];
113
+ /**
114
+ * Tracks consecutive WebSocket request failures where the HTTP retry succeeded.
115
+ * After {@link _maxConsecutiveWebSocketFallbacks} such failures, WebSocket requests are disabled entirely.
116
+ */
117
+ this._consecutiveWebSocketRetryFallbacks = 0;
105
118
  }
106
119
  /**
107
120
  * Note: the returned array of strings may be less than `n` (e.g., in case there were errors during streaming)
108
121
  */
109
122
  async fetchMany(opts, token) {
110
- let { debugName, endpoint: chatEndpoint, finishedCb, location, messages, requestOptions, source, telemetryProperties, userInitiatedRequest, requestKindOptions } = opts;
123
+ let { debugName, endpoint: chatEndpoint, finishedCb, location, messages, requestOptions, source, telemetryProperties, userInitiatedRequest, requestKindOptions, conversationId, turnId, useWebSocket, ignoreStatefulMarker } = opts;
124
+ if (useWebSocket && this._consecutiveWebSocketRetryFallbacks >= ChatMLFetcherImpl_1._maxConsecutiveWebSocketFallbacks) {
125
+ this._logService.debug(`[ChatWebSocketManager] Disabling WebSocket for request due to ${this._consecutiveWebSocketRetryFallbacks} consecutive WebSocket failures with successful HTTP fallback.`);
126
+ useWebSocket = false;
127
+ ignoreStatefulMarker = true;
128
+ }
111
129
  if (!telemetryProperties) {
112
130
  telemetryProperties = {};
113
131
  }
114
132
  if (!telemetryProperties.messageSource) {
115
133
  telemetryProperties.messageSource = debugName;
116
134
  }
135
+ const transport = useWebSocket ? 'websocket' : 'http';
117
136
  // TODO @lramos15 telemetry should not drive request ids
118
137
  const ourRequestId = telemetryProperties.requestId ?? telemetryProperties.messageId ?? (0, uuid_1.generateUuid)();
119
138
  const maxResponseTokens = chatEndpoint.maxOutputTokens;
@@ -127,6 +146,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
127
146
  const postOptions = this.preparePostOptions(requestOptions);
128
147
  const requestBody = chatEndpoint.createRequestBody({
129
148
  ...opts,
149
+ ignoreStatefulMarker,
130
150
  requestId: ourRequestId,
131
151
  postOptions
132
152
  });
@@ -141,7 +161,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
141
161
  ourRequestId,
142
162
  location: opts.location,
143
163
  body: requestBody,
144
- ignoreStatefulMarker: opts.ignoreStatefulMarker,
164
+ ignoreStatefulMarker,
145
165
  isConversationRequest: opts.isConversationRequest,
146
166
  customMetadata: opts.customMetadata
147
167
  });
@@ -155,6 +175,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
155
175
  let actualStatusCode;
156
176
  let suspendEventSeen;
157
177
  let resumeEventSeen;
178
+ let otelInferenceSpan;
158
179
  try {
159
180
  let response;
160
181
  const payloadValidationResult = isValidChatPayload(opts.messages, postOptions, chatEndpoint, this._configurationService, this._experimentationService);
@@ -169,13 +190,31 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
169
190
  else {
170
191
  const copilotToken = await this._authenticationService.getCopilotToken();
171
192
  usernameToScrub = copilotToken.username;
172
- const fetchResult = await this._fetchAndStreamChat(chatEndpoint, requestBody, baseTelemetry, streamRecorder.callback, requestOptions.secretKey, copilotToken, opts.location, ourRequestId, postOptions.n, token, userInitiatedRequest, telemetryProperties, opts.useFetcher, canRetryOnce, requestKindOptions);
193
+ const fetchResult = await this._fetchAndStreamChat(chatEndpoint, requestBody, baseTelemetry, streamRecorder.callback, requestOptions.secretKey, copilotToken, opts.location, ourRequestId, postOptions.n, token, userInitiatedRequest, useWebSocket, turnId, conversationId, telemetryProperties, opts.useFetcher, canRetryOnce, requestKindOptions);
173
194
  response = fetchResult.result;
174
195
  actualFetcher = fetchResult.fetcher;
175
196
  actualBytesReceived = fetchResult.bytesReceived;
176
197
  actualStatusCode = fetchResult.statusCode;
177
198
  suspendEventSeen = fetchResult.suspendEventSeen;
178
199
  resumeEventSeen = fetchResult.resumeEventSeen;
200
+ otelInferenceSpan = fetchResult.otelSpan;
201
+ // Tag span with debug name so orphaned spans (title, progressMessages, etc.) are identifiable
202
+ otelInferenceSpan?.setAttribute(index_1.GenAiAttr.AGENT_NAME, debugName);
203
+ // Capture request content when enabled
204
+ if (this._otelService.config.captureContent && otelInferenceSpan) {
205
+ const capiMessages = requestBody.messages;
206
+ if (capiMessages) {
207
+ otelInferenceSpan.setAttribute(index_1.GenAiAttr.INPUT_MESSAGES, (0, index_1.truncateForOTel)(JSON.stringify((0, index_1.toInputMessages)(capiMessages))));
208
+ }
209
+ // Capture system instructions (first system message)
210
+ const systemMsg = capiMessages?.find(m => m.role === 'system');
211
+ if (systemMsg?.content) {
212
+ const sysInstructions = (0, index_1.toSystemInstructions)(systemMsg.content);
213
+ if (sysInstructions) {
214
+ otelInferenceSpan.setAttribute(index_1.GenAiAttr.SYSTEM_INSTRUCTIONS, (0, index_1.truncateForOTel)(JSON.stringify(sysInstructions)));
215
+ }
216
+ }
217
+ }
179
218
  tokenCount = await chatEndpoint.acquireTokenizer().countMessagesTokens(messages);
180
219
  const extensionId = source?.extensionId ?? constants_1.EXTENSION_ID;
181
220
  this._onDidMakeChatMLRequest.fire({
@@ -189,7 +228,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
189
228
  pendingLoggedChatRequest?.markTimeToFirstToken(timeToFirstToken);
190
229
  switch (response.type) {
191
230
  case fetch_2.FetchResponseKind.Success: {
192
- const result = await this.processSuccessfulResponse(response, messages, requestBody, ourRequestId, maxResponseTokens, tokenCount, timeToFirstToken, streamRecorder, baseTelemetry, chatEndpoint, userInitiatedRequest, actualFetcher, actualBytesReceived, suspendEventSeen, resumeEventSeen);
231
+ const result = await this.processSuccessfulResponse(response, messages, requestBody, ourRequestId, maxResponseTokens, tokenCount, timeToFirstToken, streamRecorder, baseTelemetry, chatEndpoint, userInitiatedRequest, transport, actualFetcher, actualBytesReceived, suspendEventSeen, resumeEventSeen);
193
232
  // Handle FilteredRetry case with augmented messages
194
233
  if (result.type === commonTypes_1.ChatFetchResponseType.FilteredRetry) {
195
234
  if (opts.enableRetryOnFilter) {
@@ -237,6 +276,72 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
237
276
  };
238
277
  }
239
278
  pendingLoggedChatRequest?.resolve(result, streamRecorder.deltas);
279
+ // Record OTel token usage metrics if available
280
+ if (result.type === commonTypes_1.ChatFetchResponseType.Success && result.usage) {
281
+ const metricAttrs = {
282
+ operationName: index_1.GenAiOperationName.CHAT,
283
+ providerName: index_1.GenAiProviderName.GITHUB,
284
+ requestModel: chatEndpoint.model,
285
+ responseModel: result.resolvedModel,
286
+ };
287
+ if (result.usage.prompt_tokens) {
288
+ index_1.GenAiMetrics.recordTokenUsage(this._otelService, result.usage.prompt_tokens, 'input', metricAttrs);
289
+ }
290
+ if (result.usage.completion_tokens) {
291
+ index_1.GenAiMetrics.recordTokenUsage(this._otelService, result.usage.completion_tokens, 'output', metricAttrs);
292
+ }
293
+ // Set token usage and response details on the chat span before ending it
294
+ otelInferenceSpan?.setAttributes({
295
+ [index_1.GenAiAttr.USAGE_INPUT_TOKENS]: result.usage.prompt_tokens ?? 0,
296
+ [index_1.GenAiAttr.USAGE_OUTPUT_TOKENS]: result.usage.completion_tokens ?? 0,
297
+ [index_1.GenAiAttr.RESPONSE_MODEL]: result.resolvedModel ?? chatEndpoint.model,
298
+ [index_1.GenAiAttr.RESPONSE_ID]: result.requestId,
299
+ [index_1.GenAiAttr.RESPONSE_FINISH_REASONS]: ['stop'],
300
+ ...(result.usage.prompt_tokens_details?.cached_tokens
301
+ ? { [index_1.GenAiAttr.USAGE_CACHE_READ_INPUT_TOKENS]: result.usage.prompt_tokens_details.cached_tokens }
302
+ : {}),
303
+ [index_1.CopilotChatAttr.TIME_TO_FIRST_TOKEN]: timeToFirstToken,
304
+ });
305
+ }
306
+ // Capture response content when enabled
307
+ if (this._otelService.config.captureContent && otelInferenceSpan && result.type === commonTypes_1.ChatFetchResponseType.Success) {
308
+ const responseText = streamRecorder.deltas.map(d => d.text).join('');
309
+ const toolCalls = streamRecorder.deltas
310
+ .filter(d => d.copilotToolCalls?.length)
311
+ .flatMap(d => d.copilotToolCalls.map(tc => ({
312
+ type: 'tool_call', id: tc.id, name: tc.name, arguments: tc.arguments
313
+ })));
314
+ const parts = [];
315
+ if (responseText) {
316
+ parts.push({ type: 'text', content: responseText });
317
+ }
318
+ parts.push(...toolCalls);
319
+ if (parts.length > 0) {
320
+ otelInferenceSpan.setAttribute(index_1.GenAiAttr.OUTPUT_MESSAGES, (0, index_1.truncateForOTel)(JSON.stringify([{ role: 'assistant', parts }])));
321
+ }
322
+ }
323
+ // Emit OTel inference details event BEFORE ending the span
324
+ // so the log record inherits the active trace context
325
+ (0, index_1.emitInferenceDetailsEvent)(this._otelService, {
326
+ model: chatEndpoint.model,
327
+ temperature: requestOptions?.temperature,
328
+ maxTokens: requestOptions?.max_tokens,
329
+ }, result.type === commonTypes_1.ChatFetchResponseType.Success ? {
330
+ id: result.requestId,
331
+ model: result.resolvedModel,
332
+ finishReasons: ['stop'],
333
+ inputTokens: result.usage?.prompt_tokens,
334
+ outputTokens: result.usage?.completion_tokens,
335
+ } : undefined);
336
+ otelInferenceSpan?.end();
337
+ otelInferenceSpan = undefined;
338
+ // Record OTel time-to-first-token metric
339
+ if (timeToFirstToken > 0) {
340
+ index_1.GenAiMetrics.recordTimeToFirstToken(this._otelService, chatEndpoint.model, timeToFirstToken / 1000);
341
+ }
342
+ if (useWebSocket && result.type === commonTypes_1.ChatFetchResponseType.Success) {
343
+ this._consecutiveWebSocketRetryFallbacks = 0;
344
+ }
240
345
  return result;
241
346
  }
242
347
  case fetch_2.FetchResponseKind.Canceled:
@@ -245,6 +350,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
245
350
  requestId: ourRequestId,
246
351
  model: chatEndpoint.model,
247
352
  apiType: chatEndpoint.apiType,
353
+ transport,
248
354
  associatedRequestId: telemetryProperties.associatedRequestId,
249
355
  retryAfterError: telemetryProperties.retryAfterError,
250
356
  retryAfterErrorGitHubRequestId: telemetryProperties.retryAfterErrorGitHubRequestId,
@@ -276,7 +382,9 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
276
382
  const statusCodesToRetry = retryServerErrorStatusCodes
277
383
  .split(',')
278
384
  .map(s => parseInt(s.trim(), 10));
279
- if (enableRetryOnError && actualStatusCode !== undefined && statusCodesToRetry.includes(actualStatusCode)) {
385
+ const retryAfterServerError = enableRetryOnError && actualStatusCode !== undefined && statusCodesToRetry.includes(actualStatusCode);
386
+ const retryWithoutWebSocket = enableRetryOnError && useWebSocket;
387
+ if (retryAfterServerError || retryWithoutWebSocket) {
280
388
  const { retryResult } = await this._retryAfterError({
281
389
  opts,
282
390
  processed,
@@ -285,6 +393,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
285
393
  tokenCount,
286
394
  maxResponseTokens,
287
395
  timeToError: timeToFirstToken,
396
+ transport,
288
397
  actualFetcher,
289
398
  bytesReceived: actualBytesReceived,
290
399
  baseTelemetry,
@@ -310,6 +419,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
310
419
  maxResponseTokens,
311
420
  timeToFirstToken,
312
421
  isVisionRequest: this.filterImageMessages(messages),
422
+ transport,
313
423
  fetcher: actualFetcher,
314
424
  bytesReceived: actualBytesReceived,
315
425
  issuedTime: baseTelemetry.issuedTime,
@@ -323,6 +433,8 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
323
433
  }
324
434
  }
325
435
  catch (err) {
436
+ // End OTel inference span on error if not already ended
437
+ otelInferenceSpan?.end();
326
438
  const timeToError = Date.now() - baseTelemetry.issuedTime;
327
439
  if (err.fetcherId) {
328
440
  actualFetcher = err.fetcherId;
@@ -334,34 +446,34 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
334
446
  resumeEventSeen = true;
335
447
  }
336
448
  const processed = this.processError(err, ourRequestId, err.gitHubRequestId, usernameToScrub);
337
- if (processed.type === commonTypes_1.ChatFetchResponseType.NetworkError && enableRetryOnError) {
338
- const isRetryNetworkErrorEnabled = this._configurationService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.RetryNetworkErrors, this._experimentationService);
339
- if (isRetryNetworkErrorEnabled) {
340
- const { retryResult, connectivityTestError, connectivityTestErrorGitHubRequestId } = await this._retryAfterError({
341
- opts,
342
- processed,
343
- telemetryProperties,
344
- requestBody,
345
- tokenCount,
346
- maxResponseTokens,
347
- timeToError,
348
- actualFetcher,
349
- bytesReceived: err.bytesReceived,
350
- baseTelemetry,
351
- streamRecorder,
352
- retryReason: 'network_error',
353
- debugNamePrefix: 'retry-error-',
354
- pendingLoggedChatRequest,
355
- token,
356
- usernameToScrub,
357
- suspendEventSeen,
358
- resumeEventSeen,
359
- });
360
- if (retryResult) {
361
- return retryResult;
362
- }
363
- telemetryProperties = { ...telemetryProperties, connectivityTestError, connectivityTestErrorGitHubRequestId };
449
+ const retryNetworkError = enableRetryOnError && processed.type === commonTypes_1.ChatFetchResponseType.NetworkError && this._configurationService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.RetryNetworkErrors, this._experimentationService);
450
+ const retryWithoutWebSocket = enableRetryOnError && useWebSocket && (processed.type === commonTypes_1.ChatFetchResponseType.NetworkError || processed.type === commonTypes_1.ChatFetchResponseType.Failed);
451
+ if (retryNetworkError || retryWithoutWebSocket) {
452
+ const { retryResult, connectivityTestError, connectivityTestErrorGitHubRequestId } = await this._retryAfterError({
453
+ opts,
454
+ processed,
455
+ telemetryProperties,
456
+ requestBody,
457
+ tokenCount,
458
+ maxResponseTokens,
459
+ timeToError,
460
+ transport,
461
+ actualFetcher,
462
+ bytesReceived: err.bytesReceived,
463
+ baseTelemetry,
464
+ streamRecorder,
465
+ retryReason: 'network_error',
466
+ debugNamePrefix: 'retry-error-',
467
+ pendingLoggedChatRequest,
468
+ token,
469
+ usernameToScrub,
470
+ suspendEventSeen,
471
+ resumeEventSeen,
472
+ });
473
+ if (retryResult) {
474
+ return retryResult;
364
475
  }
476
+ telemetryProperties = { ...telemetryProperties, connectivityTestError, connectivityTestErrorGitHubRequestId };
365
477
  }
366
478
  if (processed.type === commonTypes_1.ChatFetchResponseType.Canceled) {
367
479
  chatMLFetcherTelemetry_1.ChatMLFetcherTelemetrySender.sendCancellationTelemetry(this._telemetryService, {
@@ -369,6 +481,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
369
481
  requestId: ourRequestId,
370
482
  model: chatEndpoint.model,
371
483
  apiType: chatEndpoint.apiType,
484
+ transport,
372
485
  associatedRequestId: telemetryProperties.associatedRequestId,
373
486
  retryAfterError: telemetryProperties.retryAfterError,
374
487
  retryAfterErrorGitHubRequestId: telemetryProperties.retryAfterErrorGitHubRequestId,
@@ -401,6 +514,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
401
514
  maxResponseTokens,
402
515
  timeToFirstToken: timeToError,
403
516
  isVisionRequest: this.filterImageMessages(messages),
517
+ transport,
404
518
  fetcher: actualFetcher,
405
519
  bytesReceived: err.bytesReceived,
406
520
  issuedTime: baseTelemetry.issuedTime,
@@ -468,7 +582,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
468
582
  return authHeaders;
469
583
  }
470
584
  async _retryAfterError(params) {
471
- const { opts, processed, telemetryProperties, requestBody, tokenCount, maxResponseTokens, timeToError, actualFetcher, bytesReceived, baseTelemetry, streamRecorder, retryReason, debugNamePrefix, pendingLoggedChatRequest, token, usernameToScrub, suspendEventSeen, resumeEventSeen, } = params;
585
+ const { opts, processed, telemetryProperties, requestBody, tokenCount, maxResponseTokens, timeToError, transport, actualFetcher, bytesReceived, baseTelemetry, streamRecorder, retryReason, debugNamePrefix, pendingLoggedChatRequest, token, usernameToScrub, suspendEventSeen, resumeEventSeen, } = params;
472
586
  // net::ERR_NETWORK_CHANGED: https://github.com/microsoft/vscode/issues/260297
473
587
  const isNetworkChangedError = ['darwin', 'linux'].includes(process.platform) && processed.reason.indexOf('net::ERR_NETWORK_CHANGED') !== -1;
474
588
  // When Electron's network process crashes, all requests through it fail permanently.
@@ -495,6 +609,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
495
609
  maxResponseTokens,
496
610
  timeToFirstToken: timeToError,
497
611
  isVisionRequest: this.filterImageMessages(opts.messages),
612
+ transport,
498
613
  fetcher: actualFetcher,
499
614
  bytesReceived,
500
615
  issuedTime: baseTelemetry.issuedTime,
@@ -505,6 +620,8 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
505
620
  streamRecorder.callback('', 0, { text: '', retryReason });
506
621
  const retryResult = await this.fetchMany({
507
622
  ...opts,
623
+ useWebSocket: false,
624
+ ignoreStatefulMarker: opts.useWebSocket || opts.ignoreStatefulMarker,
508
625
  debugName: debugNamePrefix + opts.debugName,
509
626
  userInitiatedRequest: false, // do not mark the retry as user initiated
510
627
  telemetryProperties: {
@@ -518,9 +635,17 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
518
635
  useFetcher,
519
636
  }, token);
520
637
  pendingLoggedChatRequest?.resolve(retryResult, streamRecorder.deltas);
638
+ if (opts.useWebSocket && retryResult.type === commonTypes_1.ChatFetchResponseType.Success) {
639
+ this._consecutiveWebSocketRetryFallbacks++;
640
+ this._logService.info(`[ChatWebSocketManager] WebSocket request failed with successful HTTP fallback (${this._consecutiveWebSocketRetryFallbacks} consecutive).`);
641
+ if (opts.conversationId && opts.turnId) {
642
+ // Closing here because the retry is transparent.
643
+ this._webSocketManager.closeConnection(opts.conversationId, opts.turnId);
644
+ }
645
+ }
521
646
  return { retryResult, connectivityTestError, connectivityTestErrorGitHubRequestId };
522
647
  }
523
- async _fetchAndStreamChat(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, copilotToken, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions) {
648
+ async _fetchAndStreamChat(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, copilotToken, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, useWebSocket, turnId, conversationId, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions) {
524
649
  const isPowerSaveBlockerEnabled = this._configurationService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.ChatRequestPowerSaveBlocker, this._experimentationService);
525
650
  const blockerHandle = isPowerSaveBlockerEnabled && location !== commonTypes_1.ChatLocation.Other ? this._powerService.acquirePowerSaveBlocker() : undefined;
526
651
  let suspendEventSeen = false;
@@ -534,7 +659,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
534
659
  this._logService.info(`System resumed during streaming request ${ourRequestId} (${commonTypes_1.ChatLocation.toString(location)})`);
535
660
  });
536
661
  try {
537
- const fetchResult = await this._doFetchAndStreamChat(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, copilotToken, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions);
662
+ const fetchResult = await this._doFetchAndStreamChat(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, copilotToken, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, useWebSocket, turnId, conversationId, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions);
538
663
  return { ...fetchResult, suspendEventSeen: suspendEventSeen || undefined, resumeEventSeen: resumeEventSeen || undefined };
539
664
  }
540
665
  catch (err) {
@@ -552,28 +677,187 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
552
677
  blockerHandle?.dispose();
553
678
  }
554
679
  }
555
- async _doFetchAndStreamChat(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, copilotToken, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions) {
680
+ async _doFetchAndStreamChat(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, copilotToken, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, useWebSocket, turnId, conversationId, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions) {
556
681
  if (cancellationToken.isCancellationRequested) {
557
682
  return { result: { type: fetch_2.FetchResponseKind.Canceled, reason: 'before fetch request' } };
558
683
  }
559
- this._logService.debug(`modelMaxPromptTokens ${chatEndpointInfo.modelMaxPromptTokens}`);
560
- this._logService.debug(`modelMaxResponseTokens ${request.max_tokens ?? 2048}`);
561
- this._logService.debug(`chat model ${chatEndpointInfo.model}`);
562
- secretKey ??= copilotToken.token;
563
- if (!secretKey) {
564
- // If no key is set we error
565
- const urlOrRequestMetadata = (0, networking_1.stringifyUrlOrRequestMetadata)(chatEndpointInfo.urlOrRequestMetadata);
566
- this._logService.error(`Failed to send request to ${urlOrRequestMetadata} due to missing key`);
567
- (0, stream_1.sendCommunicationErrorTelemetry)(this._telemetryService, `Failed to send request to ${urlOrRequestMetadata} due to missing key`);
568
- return {
569
- result: {
570
- type: fetch_2.FetchResponseKind.Failed,
571
- modelRequestId: undefined,
572
- failKind: fetch_2.ChatFailKind.TokenExpiredOrInvalid,
573
- reason: 'key is missing'
574
- }
575
- };
684
+ // OTel inference span for this LLM call
685
+ const serverAddress = typeof chatEndpointInfo.urlOrRequestMetadata === 'string'
686
+ ? (() => { try {
687
+ return new URL(chatEndpointInfo.urlOrRequestMetadata).hostname;
688
+ }
689
+ catch {
690
+ return undefined;
691
+ } })()
692
+ : undefined;
693
+ const otelSpan = this._otelService.startSpan(`chat ${chatEndpointInfo.model}`, {
694
+ kind: 2 /* SpanKind.CLIENT */,
695
+ attributes: {
696
+ [index_1.GenAiAttr.OPERATION_NAME]: index_1.GenAiOperationName.CHAT,
697
+ [index_1.GenAiAttr.PROVIDER_NAME]: index_1.GenAiProviderName.GITHUB,
698
+ [index_1.GenAiAttr.REQUEST_MODEL]: chatEndpointInfo.model,
699
+ [index_1.GenAiAttr.CONVERSATION_ID]: telemetryProperties?.requestId ?? ourRequestId,
700
+ [index_1.GenAiAttr.REQUEST_MAX_TOKENS]: request.max_tokens ?? request.max_output_tokens ?? request.max_completion_tokens ?? 2048,
701
+ ...(request.temperature !== undefined ? { [index_1.GenAiAttr.REQUEST_TEMPERATURE]: request.temperature } : {}),
702
+ ...(request.top_p !== undefined ? { [index_1.GenAiAttr.REQUEST_TOP_P]: request.top_p } : {}),
703
+ [index_1.CopilotChatAttr.MAX_PROMPT_TOKENS]: chatEndpointInfo.modelMaxPromptTokens,
704
+ ...(serverAddress ? { [index_1.StdAttr.SERVER_ADDRESS]: serverAddress } : {}),
705
+ },
706
+ });
707
+ const otelStartTime = Date.now();
708
+ try {
709
+ this._logService.debug(`modelMaxPromptTokens ${chatEndpointInfo.modelMaxPromptTokens}`);
710
+ this._logService.debug(`modelMaxResponseTokens ${request.max_tokens ?? 2048}`);
711
+ this._logService.debug(`chat model ${chatEndpointInfo.model}`);
712
+ secretKey ??= copilotToken.token;
713
+ if (!secretKey) {
714
+ // If no key is set we error
715
+ const urlOrRequestMetadata = (0, networking_1.stringifyUrlOrRequestMetadata)(chatEndpointInfo.urlOrRequestMetadata);
716
+ this._logService.error(`Failed to send request to ${urlOrRequestMetadata} due to missing key`);
717
+ (0, stream_1.sendCommunicationErrorTelemetry)(this._telemetryService, `Failed to send request to ${urlOrRequestMetadata} due to missing key`);
718
+ return {
719
+ result: {
720
+ type: fetch_2.FetchResponseKind.Failed,
721
+ modelRequestId: undefined,
722
+ failKind: fetch_2.ChatFailKind.TokenExpiredOrInvalid,
723
+ reason: 'key is missing'
724
+ }
725
+ };
726
+ }
727
+ // WebSocket path: use persistent WebSocket connection for Responses API endpoints
728
+ if (useWebSocket && turnId && conversationId) {
729
+ const wsResult = await this._doFetchViaWebSocket(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, location, ourRequestId, turnId, conversationId, cancellationToken, userInitiatedRequest, telemetryProperties, requestKindOptions);
730
+ return { ...wsResult, otelSpan };
731
+ }
732
+ const httpResult = await this._doFetchViaHttp(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions);
733
+ return { ...httpResult, otelSpan };
576
734
  }
735
+ catch (err) {
736
+ otelSpan.setStatus(2 /* SpanStatusCode.ERROR */, err instanceof Error ? err.message : String(err));
737
+ otelSpan.setAttribute(index_1.StdAttr.ERROR_TYPE, err instanceof Error ? err.constructor.name : 'Error');
738
+ otelSpan.recordException(err);
739
+ throw err;
740
+ }
741
+ finally {
742
+ const durationSec = (Date.now() - otelStartTime) / 1000;
743
+ index_1.GenAiMetrics.recordOperationDuration(this._otelService, durationSec, {
744
+ operationName: index_1.GenAiOperationName.CHAT,
745
+ providerName: index_1.GenAiProviderName.GITHUB,
746
+ requestModel: chatEndpointInfo.model,
747
+ });
748
+ // Span is NOT ended here — caller (fetchMany) will set token attributes and end it
749
+ }
750
+ }
751
+ /**
752
+ * Sends a chat request via a persistent WebSocket connection instead of HTTP POST.
753
+ * Events are the same Responses API streaming events, processed by OpenAIResponsesProcessor.
754
+ */
755
+ async _doFetchViaWebSocket(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, location, ourRequestId, turnId, conversationId, cancellationToken, userInitiatedRequest, telemetryProperties, requestKindOptions) {
756
+ const intent = locationToIntent(location);
757
+ const agentInteractionType = requestKindOptions?.kind === 'subagent' ?
758
+ 'conversation-subagent' :
759
+ requestKindOptions?.kind === 'background' ?
760
+ 'conversation-background' :
761
+ intent === 'conversation-agent' ? intent : undefined;
762
+ const additionalHeaders = {
763
+ 'Authorization': `Bearer ${secretKey}`,
764
+ 'X-Request-Id': ourRequestId,
765
+ 'OpenAI-Intent': intent,
766
+ 'X-GitHub-Api-Version': '2025-05-01',
767
+ 'X-Interaction-Id': this._interactionService.interactionId,
768
+ 'X-Initiator': userInitiatedRequest ? 'user' : 'agent',
769
+ ...(chatEndpointInfo.getExtraHeaders ? chatEndpointInfo.getExtraHeaders(location) : {}),
770
+ };
771
+ if (agentInteractionType) {
772
+ additionalHeaders['X-Interaction-Type'] = agentInteractionType;
773
+ additionalHeaders['X-Agent-Task-Id'] = ourRequestId;
774
+ }
775
+ if (request.messages?.some((m) => Array.isArray(m.content) ? m.content.some(c => 'image_url' in c) : false) && chatEndpointInfo.supportsVision) {
776
+ additionalHeaders['Copilot-Vision-Request'] = 'true';
777
+ }
778
+ const connection = await this._webSocketManager.getOrCreateConnection(conversationId, turnId, additionalHeaders);
779
+ // Generate unique ID to link input and output messages
780
+ const modelCallId = (0, uuid_1.generateUuid)();
781
+ const telemetryData = telemetryData_1.TelemetryData.createAndMarkAsIssued({
782
+ endpoint: 'completions',
783
+ engineName: 'chat',
784
+ uiKind: commonTypes_1.ChatLocation.toString(location),
785
+ transport: 'websocket',
786
+ ...{ ...telemetryProperties, modelCallId },
787
+ }, {
788
+ maxTokenWindow: chatEndpointInfo.modelMaxPromptTokens
789
+ });
790
+ const modelRequestId = (0, fetch_1.getRequestId)(connection.responseHeaders);
791
+ telemetryData.extendWithRequestId(modelRequestId);
792
+ for (const [key, value] of Object.entries(request)) {
793
+ if (key === 'messages' || key === 'input') {
794
+ continue;
795
+ } // Skip messages (PII)
796
+ telemetryData.properties[`request.option.${key}`] = JSON.stringify(value) ?? 'undefined';
797
+ }
798
+ telemetryData.properties['headerRequestId'] = ourRequestId;
799
+ this._telemetryService.sendGHTelemetryEvent('request.sent', telemetryData.properties, telemetryData.measurements);
800
+ const requestStart = Date.now();
801
+ const handle = connection.sendRequest(request, cancellationToken);
802
+ const extendedBaseTelemetryData = baseTelemetryData.extendedBy({ modelCallId });
803
+ const processor = this._instantiationService.createInstance(responsesApi_1.OpenAIResponsesProcessor, extendedBaseTelemetryData, modelRequestId.headerRequestId, modelRequestId.gitHubRequestId);
804
+ const chatCompletions = new async_1.AsyncIterableObject(async (emitter) => {
805
+ try {
806
+ await new Promise((resolve, reject) => {
807
+ handle.onEvent(event => {
808
+ const completion = processor.push(event, finishedCb);
809
+ if (completion) {
810
+ (0, responsesApi_1.sendCompletionOutputTelemetry)(this._telemetryService, this._logService, completion, extendedBaseTelemetryData);
811
+ emitter.emitOne(completion);
812
+ }
813
+ });
814
+ handle.onError(error => {
815
+ error.gitHubRequestId = modelRequestId.gitHubRequestId;
816
+ if ((0, errors_2.isCancellationError)(error)) {
817
+ reject(error);
818
+ return;
819
+ }
820
+ const warningTelemetry = telemetryData.extendedBy({ error: error.message });
821
+ this._telemetryService.sendGHTelemetryEvent('request.shownWarning', warningTelemetry.properties, warningTelemetry.measurements);
822
+ const totalTimeMs = Date.now() - requestStart;
823
+ telemetryData.measurements.totalTimeMs = totalTimeMs;
824
+ telemetryData.properties.error = error.message;
825
+ this._logService.debug(`request.error: [websocket], took ${totalTimeMs} ms`);
826
+ this._telemetryService.sendGHTelemetryEvent('request.error', telemetryData.properties, telemetryData.measurements);
827
+ reject(error);
828
+ });
829
+ handle.onComplete(() => {
830
+ const totalTimeMs = Date.now() - requestStart;
831
+ telemetryData.measurements.totalTimeMs = totalTimeMs;
832
+ this._logService.debug(`request.response: [websocket], took ${totalTimeMs} ms`);
833
+ this._telemetryService.sendGHTelemetryEvent('request.response', telemetryData.properties, telemetryData.measurements);
834
+ resolve();
835
+ });
836
+ });
837
+ }
838
+ finally {
839
+ let messagesToLog = request.messages;
840
+ if ((!messagesToLog || messagesToLog.length === 0) && request.input) {
841
+ try {
842
+ const rawMessages = (0, responsesApi_1.responseApiInputToRawMessagesForLogging)(request);
843
+ messagesToLog = (0, openai_1.rawMessageToCAPI)(rawMessages);
844
+ }
845
+ catch (e) {
846
+ this._logService.error(`Failed to convert Response API input to messages for telemetry:`, e);
847
+ messagesToLog = [];
848
+ }
849
+ }
850
+ (0, chatStream_1.sendEngineMessagesTelemetry)(this._telemetryService, messagesToLog ?? [], telemetryData, false, this._logService);
851
+ }
852
+ });
853
+ return {
854
+ result: {
855
+ type: fetch_2.FetchResponseKind.Success,
856
+ chatCompletions,
857
+ }
858
+ };
859
+ }
860
+ async _doFetchViaHttp(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, telemetryProperties, useFetcher, canRetryOnce, requestKindOptions) {
577
861
  // Generate unique ID to link input and output messages
578
862
  const modelCallId = (0, uuid_1.generateUuid)();
579
863
  const response = await this._fetchWithInstrumentation(chatEndpointInfo, ourRequestId, request, secretKey, location, cancellationToken, userInitiatedRequest, { ...telemetryProperties, modelCallId }, useFetcher, canRetryOnce, requestKindOptions);
@@ -660,6 +944,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
660
944
  endpoint: 'completions',
661
945
  engineName: 'chat',
662
946
  uiKind: commonTypes_1.ChatLocation.toString(location),
947
+ transport: 'http',
663
948
  ...telemetryProperties // This includes the modelCallId from fetchAndStreamChat
664
949
  }, {
665
950
  maxTokenWindow: chatEndpoint.modelMaxPromptTokens
@@ -946,7 +1231,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
946
1231
  reason: `Request Failed: ${response.status} ${text}`
947
1232
  };
948
1233
  }
949
- async processSuccessfulResponse(response, messages, requestBody, requestId, maxResponseTokens, promptTokenCount, timeToFirstToken, streamRecorder, baseTelemetry, chatEndpointInfo, userInitiatedRequest, fetcher, bytesReceived, suspendEventSeen, resumeEventSeen) {
1234
+ async processSuccessfulResponse(response, messages, requestBody, requestId, maxResponseTokens, promptTokenCount, timeToFirstToken, streamRecorder, baseTelemetry, chatEndpointInfo, userInitiatedRequest, transport, fetcher, bytesReceived, suspendEventSeen, resumeEventSeen) {
950
1235
  const completions = [];
951
1236
  for await (const chatCompletion of response.chatCompletions) {
952
1237
  chatMLFetcherTelemetry_1.ChatMLFetcherTelemetrySender.sendSuccessTelemetry(this._telemetryService, {
@@ -960,6 +1245,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
960
1245
  timeToFirstToken,
961
1246
  timeToFirstTokenEmitted: (baseTelemetry && streamRecorder.firstTokenEmittedTime) ? streamRecorder.firstTokenEmittedTime - baseTelemetry.issuedTime : -1,
962
1247
  hasImageMessages: this.filterImageMessages(messages),
1248
+ transport,
963
1249
  fetcher,
964
1250
  bytesReceived,
965
1251
  suspendEventSeen,
@@ -1225,7 +1511,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
1225
1511
  }
1226
1512
  };
1227
1513
  exports.ChatMLFetcherImpl = ChatMLFetcherImpl;
1228
- exports.ChatMLFetcherImpl = ChatMLFetcherImpl = __decorate([
1514
+ exports.ChatMLFetcherImpl = ChatMLFetcherImpl = ChatMLFetcherImpl_1 = __decorate([
1229
1515
  __param(0, fetcherService_1.IFetcherService),
1230
1516
  __param(1, telemetry_1.ITelemetryService),
1231
1517
  __param(2, requestLogger_1.IRequestLogger),
@@ -1238,7 +1524,9 @@ exports.ChatMLFetcherImpl = ChatMLFetcherImpl = __decorate([
1238
1524
  __param(9, configurationService_1.IConfigurationService),
1239
1525
  __param(10, nullExperimentationService_1.IExperimentationService),
1240
1526
  __param(11, powerService_1.IPowerService),
1241
- __param(12, instantiation_1.IInstantiationService)
1527
+ __param(12, instantiation_1.IInstantiationService),
1528
+ __param(13, chatWebSocketManager_1.IChatWebSocketManager),
1529
+ __param(14, otelService_1.IOTelService)
1242
1530
  ], ChatMLFetcherImpl);
1243
1531
  /**
1244
1532
  * Validates a chat request payload to ensure it is valid