@nomomon/ai-sdk-provider-github-copilot 0.1.0 → 0.1.1
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.
- package/README.md +18 -10
- package/dist/index.cjs +288 -250
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +288 -250
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# AI SDK Provider for GitHub Copilot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/nomomon/ai-sdk-provider-github-copilot/actions)
|
|
4
|
+
[](https://codecov.io/gh/nomomon/ai-sdk-provider-github-copilot)
|
|
5
|
+
[](https://github.com/nomomon/ai-sdk-provider-github-copilot/pkgs/npm/ai-sdk-provider-github-copilot)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](https://github.com/nomomon/ai-sdk-provider-github-copilot)
|
|
9
|
+
|
|
10
|
+
Vercel AI SDK community provider for GitHub Copilot — use `streamText`, `generateText`, and related AI SDK APIs with GitHub Copilot as the backend model.
|
|
11
|
+
|
|
12
|
+
---
|
|
4
13
|
|
|
5
14
|
## Installation
|
|
6
15
|
|
|
@@ -48,13 +57,7 @@ for await (const chunk of result.textStream) {
|
|
|
48
57
|
|
|
49
58
|
## Models
|
|
50
59
|
|
|
51
|
-
Use model IDs available via Copilot CLI
|
|
52
|
-
|
|
53
|
-
- `gpt-5` - GPT-5 (when available)
|
|
54
|
-
- `claude-sonnet-4.5` - Claude Sonnet
|
|
55
|
-
- `claude-opus-4` - Claude Opus
|
|
56
|
-
|
|
57
|
-
Run `copilot models` to list available models in your environment.
|
|
60
|
+
Use model IDs available via Copilot CLI. Run `copilot -i /models` to list available models in your environment.
|
|
58
61
|
|
|
59
62
|
## Configuration
|
|
60
63
|
|
|
@@ -79,7 +82,7 @@ const model = githubCopilot("gpt-5", {
|
|
|
79
82
|
|
|
80
83
|
### Custom tools
|
|
81
84
|
|
|
82
|
-
Pass tools via provider settings using Copilot's `defineTool
|
|
85
|
+
Pass tools via provider settings using Copilot's `defineTool`. Tool support varies by model; verify with Copilot CLI or documentation.
|
|
83
86
|
|
|
84
87
|
```typescript
|
|
85
88
|
import { defineTool } from "@github/copilot-sdk";
|
|
@@ -101,6 +104,10 @@ const model = githubCopilot("gpt-5", {
|
|
|
101
104
|
});
|
|
102
105
|
```
|
|
103
106
|
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
Tests live in `tests/` (not colocated with source) to keep the published `src/` tree clean and to exclude test files from the build. Each source module has a corresponding `tests/*.test.ts` file. Run `npm test` or `npm run test:coverage` for coverage with thresholds.
|
|
110
|
+
|
|
104
111
|
## Limitations
|
|
105
112
|
|
|
106
113
|
- **Requires Copilot CLI** - Must be installed and authenticated
|
|
@@ -109,6 +116,7 @@ const model = githubCopilot("gpt-5", {
|
|
|
109
116
|
- **Unsupported parameters** - `temperature`, `maxTokens`, `topP`, etc. are not supported by the Copilot CLI and will be ignored (warnings emitted)
|
|
110
117
|
- **Structured outputs** - Native JSON schema support may be limited; consider prompt engineering for structured responses
|
|
111
118
|
- **Session-based** - Each generate/stream creates a new session; no built-in multi-session continuity across separate AI SDK calls
|
|
119
|
+
- **Model capabilities** - Tools, reasoning, and other features may vary by model; capabilities are determined by Copilot CLI
|
|
112
120
|
|
|
113
121
|
## Disclaimer
|
|
114
122
|
|
|
@@ -120,4 +128,4 @@ const model = githubCopilot("gpt-5", {
|
|
|
120
128
|
|
|
121
129
|
## License
|
|
122
130
|
|
|
123
|
-
MIT
|
|
131
|
+
[MIT](LICENSE)
|
package/dist/index.cjs
CHANGED
|
@@ -42,7 +42,7 @@ function isAbortError(error) {
|
|
|
42
42
|
}
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
|
-
function handleCopilotError(error
|
|
45
|
+
function handleCopilotError(error) {
|
|
46
46
|
if (isAbortError(error)) {
|
|
47
47
|
throw error;
|
|
48
48
|
}
|
|
@@ -57,9 +57,220 @@ function handleCopilotError(error, _context) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// src/
|
|
60
|
+
// src/conversion/map-copilot-finish-reason.ts
|
|
61
|
+
function mapCopilotFinishReason() {
|
|
62
|
+
return {
|
|
63
|
+
unified: "stop",
|
|
64
|
+
raw: void 0
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/conversion/usage.ts
|
|
69
|
+
function createEmptyUsage() {
|
|
70
|
+
return {
|
|
71
|
+
inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
|
|
72
|
+
outputTokens: { total: 0, text: void 0, reasoning: void 0 },
|
|
73
|
+
raw: void 0
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function convertCopilotUsage(event) {
|
|
77
|
+
const inputTokens = event.inputTokens ?? 0;
|
|
78
|
+
const outputTokens = event.outputTokens ?? 0;
|
|
79
|
+
const cacheRead = event.cacheReadTokens ?? 0;
|
|
80
|
+
const cacheWrite = event.cacheWriteTokens ?? 0;
|
|
81
|
+
return {
|
|
82
|
+
inputTokens: {
|
|
83
|
+
total: inputTokens + cacheRead + cacheWrite,
|
|
84
|
+
noCache: inputTokens,
|
|
85
|
+
cacheRead,
|
|
86
|
+
cacheWrite
|
|
87
|
+
},
|
|
88
|
+
outputTokens: {
|
|
89
|
+
total: outputTokens,
|
|
90
|
+
text: outputTokens,
|
|
91
|
+
reasoning: void 0
|
|
92
|
+
},
|
|
93
|
+
raw: event
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function createStreamEventHandler(params) {
|
|
97
|
+
const { controller, session } = params;
|
|
98
|
+
let textPartId;
|
|
99
|
+
let usage = createEmptyUsage();
|
|
100
|
+
const toolStates = /* @__PURE__ */ new Map();
|
|
101
|
+
const finishStream = () => {
|
|
102
|
+
if (textPartId) {
|
|
103
|
+
controller.enqueue({ type: "text-end", id: textPartId });
|
|
104
|
+
}
|
|
105
|
+
controller.enqueue({
|
|
106
|
+
type: "finish",
|
|
107
|
+
finishReason: mapCopilotFinishReason(),
|
|
108
|
+
usage
|
|
109
|
+
});
|
|
110
|
+
controller.close();
|
|
111
|
+
void session.destroy();
|
|
112
|
+
};
|
|
113
|
+
const handleError = (message) => {
|
|
114
|
+
controller.enqueue({
|
|
115
|
+
type: "error",
|
|
116
|
+
error: new Error(message)
|
|
117
|
+
});
|
|
118
|
+
controller.close();
|
|
119
|
+
void session.destroy();
|
|
120
|
+
};
|
|
121
|
+
return (event) => {
|
|
122
|
+
switch (event.type) {
|
|
123
|
+
case "assistant.message_delta": {
|
|
124
|
+
const delta = event.data.deltaContent;
|
|
125
|
+
if (delta) {
|
|
126
|
+
if (!textPartId) {
|
|
127
|
+
textPartId = providerUtils.generateId();
|
|
128
|
+
controller.enqueue({ type: "text-start", id: textPartId });
|
|
129
|
+
}
|
|
130
|
+
controller.enqueue({
|
|
131
|
+
type: "text-delta",
|
|
132
|
+
id: textPartId,
|
|
133
|
+
delta
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case "assistant.reasoning_delta": {
|
|
139
|
+
const delta = event.data.deltaContent;
|
|
140
|
+
if (delta) {
|
|
141
|
+
const reasoningId = providerUtils.generateId();
|
|
142
|
+
controller.enqueue({ type: "reasoning-start", id: reasoningId });
|
|
143
|
+
controller.enqueue({
|
|
144
|
+
type: "reasoning-delta",
|
|
145
|
+
id: reasoningId,
|
|
146
|
+
delta
|
|
147
|
+
});
|
|
148
|
+
controller.enqueue({ type: "reasoning-end", id: reasoningId });
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case "assistant.message": {
|
|
153
|
+
const { content, toolRequests } = event.data;
|
|
154
|
+
if (content && !textPartId) {
|
|
155
|
+
textPartId = providerUtils.generateId();
|
|
156
|
+
controller.enqueue({ type: "text-start", id: textPartId });
|
|
157
|
+
controller.enqueue({
|
|
158
|
+
type: "text-delta",
|
|
159
|
+
id: textPartId,
|
|
160
|
+
delta: content
|
|
161
|
+
});
|
|
162
|
+
controller.enqueue({ type: "text-end", id: textPartId });
|
|
163
|
+
}
|
|
164
|
+
if (toolRequests?.length) {
|
|
165
|
+
for (const tr of toolRequests) {
|
|
166
|
+
const toolId = tr.toolCallId;
|
|
167
|
+
let state = toolStates.get(toolId);
|
|
168
|
+
if (!state) {
|
|
169
|
+
state = {
|
|
170
|
+
name: tr.name,
|
|
171
|
+
inputStarted: false,
|
|
172
|
+
callEmitted: false
|
|
173
|
+
};
|
|
174
|
+
toolStates.set(toolId, state);
|
|
175
|
+
}
|
|
176
|
+
if (!state.inputStarted) {
|
|
177
|
+
controller.enqueue({
|
|
178
|
+
type: "tool-input-start",
|
|
179
|
+
id: toolId,
|
|
180
|
+
toolName: tr.name,
|
|
181
|
+
providerExecuted: true,
|
|
182
|
+
dynamic: true
|
|
183
|
+
});
|
|
184
|
+
state.inputStarted = true;
|
|
185
|
+
}
|
|
186
|
+
const args = tr.arguments ?? {};
|
|
187
|
+
controller.enqueue({
|
|
188
|
+
type: "tool-input-delta",
|
|
189
|
+
id: toolId,
|
|
190
|
+
delta: JSON.stringify(args)
|
|
191
|
+
});
|
|
192
|
+
controller.enqueue({ type: "tool-input-end", id: toolId });
|
|
193
|
+
if (!state.callEmitted) {
|
|
194
|
+
controller.enqueue({
|
|
195
|
+
type: "tool-call",
|
|
196
|
+
toolCallId: toolId,
|
|
197
|
+
toolName: tr.name,
|
|
198
|
+
input: typeof args === "string" ? args : JSON.stringify(args),
|
|
199
|
+
providerExecuted: true,
|
|
200
|
+
dynamic: true
|
|
201
|
+
});
|
|
202
|
+
state.callEmitted = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case "tool.execution_start": {
|
|
209
|
+
const { toolCallId, toolName } = event.data;
|
|
210
|
+
let state = toolStates.get(toolCallId);
|
|
211
|
+
if (!state) {
|
|
212
|
+
state = {
|
|
213
|
+
name: toolName,
|
|
214
|
+
inputStarted: true,
|
|
215
|
+
callEmitted: false
|
|
216
|
+
};
|
|
217
|
+
toolStates.set(toolCallId, state);
|
|
218
|
+
}
|
|
219
|
+
if (!state.callEmitted) {
|
|
220
|
+
controller.enqueue({
|
|
221
|
+
type: "tool-input-start",
|
|
222
|
+
id: toolCallId,
|
|
223
|
+
toolName,
|
|
224
|
+
providerExecuted: true,
|
|
225
|
+
dynamic: true
|
|
226
|
+
});
|
|
227
|
+
controller.enqueue({ type: "tool-input-end", id: toolCallId });
|
|
228
|
+
controller.enqueue({
|
|
229
|
+
type: "tool-call",
|
|
230
|
+
toolCallId,
|
|
231
|
+
toolName,
|
|
232
|
+
input: "{}",
|
|
233
|
+
providerExecuted: true,
|
|
234
|
+
dynamic: true
|
|
235
|
+
});
|
|
236
|
+
state.callEmitted = true;
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case "tool.execution_complete": {
|
|
241
|
+
const { toolCallId, success, result, error } = event.data;
|
|
242
|
+
const toolNameStr = toolStates.get(toolCallId)?.name ?? "unknown";
|
|
243
|
+
const resultContent = success && result?.content ? result.content : error?.message ?? "Tool execution failed";
|
|
244
|
+
controller.enqueue({
|
|
245
|
+
type: "tool-result",
|
|
246
|
+
toolCallId,
|
|
247
|
+
toolName: toolNameStr,
|
|
248
|
+
result: resultContent,
|
|
249
|
+
isError: !success,
|
|
250
|
+
dynamic: true
|
|
251
|
+
});
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
case "assistant.usage": {
|
|
255
|
+
usage = convertCopilotUsage(event.data);
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
case "session.idle":
|
|
259
|
+
finishStream();
|
|
260
|
+
break;
|
|
261
|
+
case "session.error":
|
|
262
|
+
handleError(event.data.message ?? "Session error");
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/conversion/convert-to-copilot-messages.ts
|
|
61
269
|
var IMAGE_URL_WARNING = "Image URLs are not supported by this provider; supply file paths as attachments.";
|
|
62
270
|
var IMAGE_BASE64_WARNING = "Base64/image data URLs require file paths. Write to temp file and pass path, or use attachments with path.";
|
|
271
|
+
function isImagePart(part) {
|
|
272
|
+
return part.type === "image";
|
|
273
|
+
}
|
|
63
274
|
function convertToCopilotMessages(prompt) {
|
|
64
275
|
const messages = [];
|
|
65
276
|
const warnings = [];
|
|
@@ -95,7 +306,7 @@ function convertToCopilotMessages(prompt) {
|
|
|
95
306
|
} else if (fileInfo.warning) {
|
|
96
307
|
warnings.push(fileInfo.warning);
|
|
97
308
|
}
|
|
98
|
-
} else if (part
|
|
309
|
+
} else if (isImagePart(part)) {
|
|
99
310
|
warnings.push(IMAGE_BASE64_WARNING);
|
|
100
311
|
}
|
|
101
312
|
}
|
|
@@ -181,41 +392,50 @@ function extractFileAttachment(part) {
|
|
|
181
392
|
return {};
|
|
182
393
|
}
|
|
183
394
|
|
|
184
|
-
// src/
|
|
185
|
-
function
|
|
395
|
+
// src/model/session-setup.ts
|
|
396
|
+
async function prepareSession(input) {
|
|
397
|
+
const {
|
|
398
|
+
prompt,
|
|
399
|
+
options,
|
|
400
|
+
streaming,
|
|
401
|
+
buildSessionConfig,
|
|
402
|
+
generateWarnings,
|
|
403
|
+
getClient,
|
|
404
|
+
systemMessageFromSettings
|
|
405
|
+
} = input;
|
|
406
|
+
const {
|
|
407
|
+
prompt: promptText,
|
|
408
|
+
systemMessage,
|
|
409
|
+
attachments,
|
|
410
|
+
warnings: msgWarnings
|
|
411
|
+
} = convertToCopilotMessages(prompt);
|
|
412
|
+
const warnings = [
|
|
413
|
+
...generateWarnings(options),
|
|
414
|
+
...msgWarnings?.map((m) => ({ type: "other", message: m })) ?? []
|
|
415
|
+
];
|
|
416
|
+
const client = getClient();
|
|
417
|
+
if (client.getState() !== "connected") {
|
|
418
|
+
await client.start();
|
|
419
|
+
}
|
|
420
|
+
const session = await client.createSession({
|
|
421
|
+
...buildSessionConfig(streaming),
|
|
422
|
+
systemMessage: systemMessage ? { mode: "append", content: systemMessage } : systemMessageFromSettings
|
|
423
|
+
});
|
|
186
424
|
return {
|
|
187
|
-
|
|
188
|
-
|
|
425
|
+
prompt: promptText,
|
|
426
|
+
attachments,
|
|
427
|
+
warnings,
|
|
428
|
+
session
|
|
189
429
|
};
|
|
190
430
|
}
|
|
191
431
|
|
|
192
|
-
// src/github-copilot-language-model.ts
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
outputTokens: { total: 0, text: void 0, reasoning: void 0 },
|
|
197
|
-
raw: void 0
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
function convertCopilotUsage(event) {
|
|
201
|
-
const inputTokens = event.inputTokens ?? 0;
|
|
202
|
-
const outputTokens = event.outputTokens ?? 0;
|
|
203
|
-
const cacheRead = event.cacheReadTokens ?? 0;
|
|
204
|
-
const cacheWrite = 0;
|
|
205
|
-
return {
|
|
206
|
-
inputTokens: {
|
|
207
|
-
total: inputTokens + cacheRead + cacheWrite,
|
|
208
|
-
noCache: inputTokens,
|
|
209
|
-
cacheRead,
|
|
210
|
-
cacheWrite
|
|
211
|
-
},
|
|
212
|
-
outputTokens: {
|
|
213
|
-
total: outputTokens,
|
|
214
|
-
text: outputTokens,
|
|
215
|
-
reasoning: void 0
|
|
216
|
-
},
|
|
217
|
-
raw: event
|
|
432
|
+
// src/model/github-copilot-language-model.ts
|
|
433
|
+
var SEND_AND_WAIT_TIMEOUT_MS = 6e4;
|
|
434
|
+
function addAbortListener(signal, onAbort) {
|
|
435
|
+
if (!signal) return () => {
|
|
218
436
|
};
|
|
437
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
438
|
+
return () => signal.removeEventListener("abort", onAbort);
|
|
219
439
|
}
|
|
220
440
|
var GitHubCopilotLanguageModel = class {
|
|
221
441
|
specificationVersion = "v3";
|
|
@@ -267,34 +487,27 @@ var GitHubCopilotLanguageModel = class {
|
|
|
267
487
|
}
|
|
268
488
|
return warnings;
|
|
269
489
|
}
|
|
270
|
-
async
|
|
271
|
-
|
|
272
|
-
prompt,
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
...msgWarnings?.map((m) => ({ type: "other", message: m })) ?? []
|
|
280
|
-
];
|
|
281
|
-
const client = this.getClient();
|
|
282
|
-
if (client.getState() !== "connected") {
|
|
283
|
-
await client.start();
|
|
284
|
-
}
|
|
285
|
-
const session = await client.createSession({
|
|
286
|
-
...this.buildSessionConfig(false),
|
|
287
|
-
systemMessage: systemMessage ? { mode: "append", content: systemMessage } : this.settings.systemMessage
|
|
490
|
+
async prepareSessionForCall(options, streaming) {
|
|
491
|
+
return prepareSession({
|
|
492
|
+
prompt: options.prompt,
|
|
493
|
+
options,
|
|
494
|
+
streaming,
|
|
495
|
+
buildSessionConfig: (s) => this.buildSessionConfig(s),
|
|
496
|
+
generateWarnings: (o) => this.generateWarnings(o),
|
|
497
|
+
getClient: this.getClient,
|
|
498
|
+
systemMessageFromSettings: this.settings.systemMessage
|
|
288
499
|
});
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
options
|
|
293
|
-
|
|
500
|
+
}
|
|
501
|
+
async doGenerate(options) {
|
|
502
|
+
const { prompt, attachments, warnings, session } = await this.prepareSessionForCall(
|
|
503
|
+
options,
|
|
504
|
+
false
|
|
505
|
+
);
|
|
506
|
+
const removeAbortListener = addAbortListener(options.abortSignal, () => session.abort());
|
|
294
507
|
try {
|
|
295
508
|
const result = await session.sendAndWait(
|
|
296
509
|
{ prompt, attachments },
|
|
297
|
-
options.abortSignal?.aborted ? 0 :
|
|
510
|
+
options.abortSignal?.aborted ? 0 : SEND_AND_WAIT_TIMEOUT_MS
|
|
298
511
|
);
|
|
299
512
|
const content = [];
|
|
300
513
|
const text = result?.data?.content ?? "";
|
|
@@ -323,12 +536,10 @@ var GitHubCopilotLanguageModel = class {
|
|
|
323
536
|
if (isAbortError(error)) {
|
|
324
537
|
throw options.abortSignal?.aborted ? options.abortSignal.reason : error;
|
|
325
538
|
}
|
|
326
|
-
handleCopilotError(error
|
|
327
|
-
|
|
539
|
+
handleCopilotError(error);
|
|
540
|
+
throw new Error("Unreachable: handleCopilotError always throws");
|
|
328
541
|
} finally {
|
|
329
|
-
|
|
330
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
331
|
-
}
|
|
542
|
+
removeAbortListener();
|
|
332
543
|
try {
|
|
333
544
|
await session.destroy();
|
|
334
545
|
} catch {
|
|
@@ -336,196 +547,27 @@ var GitHubCopilotLanguageModel = class {
|
|
|
336
547
|
}
|
|
337
548
|
}
|
|
338
549
|
async doStream(options) {
|
|
339
|
-
const {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
warnings: msgWarnings
|
|
344
|
-
} = convertToCopilotMessages(options.prompt);
|
|
345
|
-
const warnings = [
|
|
346
|
-
...this.generateWarnings(options),
|
|
347
|
-
...msgWarnings?.map((m) => ({ type: "other", message: m })) ?? []
|
|
348
|
-
];
|
|
349
|
-
const client = this.getClient();
|
|
350
|
-
if (client.getState() !== "connected") {
|
|
351
|
-
await client.start();
|
|
352
|
-
}
|
|
353
|
-
const session = await client.createSession({
|
|
354
|
-
...this.buildSessionConfig(true),
|
|
355
|
-
systemMessage: systemMessage ? { mode: "append", content: systemMessage } : this.settings.systemMessage
|
|
356
|
-
});
|
|
550
|
+
const { prompt, attachments, warnings, session } = await this.prepareSessionForCall(
|
|
551
|
+
options,
|
|
552
|
+
true
|
|
553
|
+
);
|
|
357
554
|
const abortController = new AbortController();
|
|
358
|
-
let abortListener;
|
|
359
555
|
if (options.abortSignal?.aborted) {
|
|
360
556
|
abortController.abort(options.abortSignal.reason);
|
|
361
|
-
} else if (options.abortSignal) {
|
|
362
|
-
abortListener = () => {
|
|
363
|
-
session.abort();
|
|
364
|
-
abortController.abort(options.abortSignal?.reason);
|
|
365
|
-
};
|
|
366
|
-
options.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
367
557
|
}
|
|
558
|
+
const removeAbortListener = addAbortListener(options.abortSignal, () => {
|
|
559
|
+
session.abort();
|
|
560
|
+
abortController.abort(options.abortSignal?.reason);
|
|
561
|
+
});
|
|
368
562
|
const stream = new ReadableStream({
|
|
369
563
|
start: async (controller) => {
|
|
370
|
-
let textPartId;
|
|
371
|
-
let usage = createEmptyUsage();
|
|
372
|
-
const toolStates = /* @__PURE__ */ new Map();
|
|
373
564
|
try {
|
|
374
565
|
controller.enqueue({ type: "stream-start", warnings });
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (delta) {
|
|
379
|
-
if (!textPartId) {
|
|
380
|
-
textPartId = providerUtils.generateId();
|
|
381
|
-
controller.enqueue({ type: "text-start", id: textPartId });
|
|
382
|
-
}
|
|
383
|
-
controller.enqueue({
|
|
384
|
-
type: "text-delta",
|
|
385
|
-
id: textPartId,
|
|
386
|
-
delta
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
} else if (event.type === "assistant.reasoning_delta") {
|
|
390
|
-
const delta = event.data?.deltaContent;
|
|
391
|
-
if (delta) {
|
|
392
|
-
const reasoningId = providerUtils.generateId();
|
|
393
|
-
controller.enqueue({ type: "reasoning-start", id: reasoningId });
|
|
394
|
-
controller.enqueue({
|
|
395
|
-
type: "reasoning-delta",
|
|
396
|
-
id: reasoningId,
|
|
397
|
-
delta
|
|
398
|
-
});
|
|
399
|
-
controller.enqueue({ type: "reasoning-end", id: reasoningId });
|
|
400
|
-
}
|
|
401
|
-
} else if (event.type === "assistant.message") {
|
|
402
|
-
const data = event.data;
|
|
403
|
-
if (data?.content && !textPartId) {
|
|
404
|
-
textPartId = providerUtils.generateId();
|
|
405
|
-
controller.enqueue({ type: "text-start", id: textPartId });
|
|
406
|
-
controller.enqueue({
|
|
407
|
-
type: "text-delta",
|
|
408
|
-
id: textPartId,
|
|
409
|
-
delta: data.content
|
|
410
|
-
});
|
|
411
|
-
controller.enqueue({ type: "text-end", id: textPartId });
|
|
412
|
-
}
|
|
413
|
-
if (data?.toolRequests?.length) {
|
|
414
|
-
for (const tr of data.toolRequests) {
|
|
415
|
-
const toolId = tr.toolCallId;
|
|
416
|
-
let state = toolStates.get(toolId);
|
|
417
|
-
if (!state) {
|
|
418
|
-
state = {
|
|
419
|
-
name: tr.name,
|
|
420
|
-
inputStarted: false,
|
|
421
|
-
callEmitted: false
|
|
422
|
-
};
|
|
423
|
-
toolStates.set(toolId, state);
|
|
424
|
-
}
|
|
425
|
-
if (!state.inputStarted) {
|
|
426
|
-
controller.enqueue({
|
|
427
|
-
type: "tool-input-start",
|
|
428
|
-
id: toolId,
|
|
429
|
-
toolName: tr.name,
|
|
430
|
-
providerExecuted: true,
|
|
431
|
-
dynamic: true
|
|
432
|
-
});
|
|
433
|
-
state.inputStarted = true;
|
|
434
|
-
}
|
|
435
|
-
const args = tr.arguments ?? {};
|
|
436
|
-
controller.enqueue({
|
|
437
|
-
type: "tool-input-delta",
|
|
438
|
-
id: toolId,
|
|
439
|
-
delta: JSON.stringify(args)
|
|
440
|
-
});
|
|
441
|
-
controller.enqueue({ type: "tool-input-end", id: toolId });
|
|
442
|
-
if (!state.callEmitted) {
|
|
443
|
-
controller.enqueue({
|
|
444
|
-
type: "tool-call",
|
|
445
|
-
toolCallId: toolId,
|
|
446
|
-
toolName: tr.name,
|
|
447
|
-
input: typeof args === "string" ? args : JSON.stringify(args),
|
|
448
|
-
providerExecuted: true,
|
|
449
|
-
dynamic: true
|
|
450
|
-
});
|
|
451
|
-
state.callEmitted = true;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
} else if (event.type === "tool.execution_start") {
|
|
456
|
-
const data = event.data;
|
|
457
|
-
if (data) {
|
|
458
|
-
const toolId = data.toolCallId;
|
|
459
|
-
let state = toolStates.get(toolId);
|
|
460
|
-
if (!state) {
|
|
461
|
-
state = {
|
|
462
|
-
name: data.toolName,
|
|
463
|
-
inputStarted: true,
|
|
464
|
-
callEmitted: false
|
|
465
|
-
};
|
|
466
|
-
toolStates.set(toolId, state);
|
|
467
|
-
}
|
|
468
|
-
if (!state.callEmitted) {
|
|
469
|
-
controller.enqueue({
|
|
470
|
-
type: "tool-input-start",
|
|
471
|
-
id: toolId,
|
|
472
|
-
toolName: data.toolName,
|
|
473
|
-
providerExecuted: true,
|
|
474
|
-
dynamic: true
|
|
475
|
-
});
|
|
476
|
-
controller.enqueue({ type: "tool-input-end", id: toolId });
|
|
477
|
-
controller.enqueue({
|
|
478
|
-
type: "tool-call",
|
|
479
|
-
toolCallId: toolId,
|
|
480
|
-
toolName: data.toolName,
|
|
481
|
-
input: "{}",
|
|
482
|
-
providerExecuted: true,
|
|
483
|
-
dynamic: true
|
|
484
|
-
});
|
|
485
|
-
state.callEmitted = true;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
} else if (event.type === "tool.execution_complete") {
|
|
489
|
-
const evt = event;
|
|
490
|
-
const data = evt.data;
|
|
491
|
-
if (data) {
|
|
492
|
-
const toolName = data.toolName ?? "unknown";
|
|
493
|
-
const result = data.success && data.result?.content ? data.result.content : data.error?.message ?? "Tool execution failed";
|
|
494
|
-
controller.enqueue({
|
|
495
|
-
type: "tool-result",
|
|
496
|
-
toolCallId: data.toolCallId,
|
|
497
|
-
toolName,
|
|
498
|
-
result,
|
|
499
|
-
isError: !data.success,
|
|
500
|
-
dynamic: true
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
} else if (event.type === "assistant.usage") {
|
|
504
|
-
const data = event.data;
|
|
505
|
-
if (data) {
|
|
506
|
-
usage = convertCopilotUsage(data);
|
|
507
|
-
}
|
|
508
|
-
} else if (event.type === "session.idle") {
|
|
509
|
-
if (textPartId) {
|
|
510
|
-
controller.enqueue({ type: "text-end", id: textPartId });
|
|
511
|
-
}
|
|
512
|
-
controller.enqueue({
|
|
513
|
-
type: "finish",
|
|
514
|
-
finishReason: mapCopilotFinishReason(),
|
|
515
|
-
usage
|
|
516
|
-
});
|
|
517
|
-
controller.close();
|
|
518
|
-
void session.destroy();
|
|
519
|
-
} else if (event.type === "session.error") {
|
|
520
|
-
const data = event.data;
|
|
521
|
-
controller.enqueue({
|
|
522
|
-
type: "error",
|
|
523
|
-
error: new Error(data?.message ?? "Session error")
|
|
524
|
-
});
|
|
525
|
-
controller.close();
|
|
526
|
-
void session.destroy();
|
|
527
|
-
}
|
|
566
|
+
const handleEvent = createStreamEventHandler({
|
|
567
|
+
controller,
|
|
568
|
+
session
|
|
528
569
|
});
|
|
570
|
+
session.on(handleEvent);
|
|
529
571
|
await session.send({ prompt, attachments });
|
|
530
572
|
} catch (error) {
|
|
531
573
|
if (isAbortError(error)) {
|
|
@@ -534,20 +576,16 @@ var GitHubCopilotLanguageModel = class {
|
|
|
534
576
|
error: options.abortSignal?.aborted ? options.abortSignal.reason : error
|
|
535
577
|
});
|
|
536
578
|
} else {
|
|
537
|
-
handleCopilotError(error
|
|
579
|
+
handleCopilotError(error);
|
|
538
580
|
}
|
|
539
581
|
controller.close();
|
|
540
582
|
await session.destroy();
|
|
541
583
|
} finally {
|
|
542
|
-
|
|
543
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
544
|
-
}
|
|
584
|
+
removeAbortListener();
|
|
545
585
|
}
|
|
546
586
|
},
|
|
547
587
|
cancel: () => {
|
|
548
|
-
|
|
549
|
-
options.abortSignal.removeEventListener("abort", abortListener);
|
|
550
|
-
}
|
|
588
|
+
removeAbortListener();
|
|
551
589
|
}
|
|
552
590
|
});
|
|
553
591
|
return {
|