Skip to content

Commit be9c476

Browse files
authoredSep 3, 2024
feat: getFunctionCalls() -b closes (copilot-extensions#50)
1 parent bb4e12c commit be9c476

File tree

5 files changed

+172
-5
lines changed

5 files changed

+172
-5
lines changed
 

‎README.md

+17
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,23 @@ await prompt({
409409
});
410410
```
411411
412+
### `getFunctionCalls()`
413+
414+
Convenience metthod if a result from a `prompt()` call includes function calls.
415+
416+
```js
417+
import { prompt, getFunctionCalls } from "@copilot-extensions/preview-sdk";
418+
419+
const result = await prompt(options);
420+
const [functionCall] = getFunctionCalls(result);
421+
422+
if (functionCall) {
423+
console.log("Received a function call", functionCall);
424+
} else {
425+
console.log("No function call received");
426+
}
427+
```
428+
412429
## Dreamcode
413430
414431
While implementing the lower-level functionality, we also dream big: what would our dream SDK for Coplitot extensions look like? Please have a look and share your thoughts and ideas:

‎index.d.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,16 @@ interface PromptInterface {
306306
(options: WithRequired<PromptOptions, "messages">): Promise<PromptResult>;
307307
}
308308

309+
interface GetFunctionCallsInterface {
310+
(payload: PromptResult): {
311+
id: string;
312+
function: {
313+
name: string,
314+
arguments: string,
315+
}
316+
}[]
317+
}
318+
309319
// exported methods
310320

311321
export declare const verifyRequest: VerifyRequestInterface;
@@ -325,4 +335,5 @@ export declare const verifyAndParseRequest: VerifyAndParseRequestInterface;
325335
export declare const getUserMessage: GetUserMessageInterface;
326336
export declare const getUserConfirmation: GetUserConfirmationInterface;
327337

328-
export declare const prompt: PromptInterface;
338+
export declare const prompt: PromptInterface;
339+
export declare const getFunctionCalls: GetFunctionCallsInterface;

‎index.test-d.ts

+13
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
type InteropMessage,
2121
CopilotRequestPayload,
2222
prompt,
23+
PromptResult,
24+
getFunctionCalls,
2325
} from "./index.js";
2426

2527
const token = "";
@@ -335,4 +337,15 @@ export async function promptWithoutMessageButMessages() {
335337
{ role: "user", content: "What about Spain?" },
336338
],
337339
});
340+
}
341+
342+
export async function getFunctionCallsTest(promptResponsePayload: PromptResult) {
343+
const result = getFunctionCalls(promptResponsePayload)
344+
345+
expectType<{
346+
id: string, function: {
347+
name: string,
348+
arguments: string,
349+
}
350+
}[]>(result)
338351
}

‎lib/prompt.js

+31-3
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,38 @@ export async function prompt(userPrompt, promptOptions) {
4747
}
4848
);
4949

50-
const data = await response.json();
50+
if (response.ok) {
51+
const data = await response.json();
5152

53+
return {
54+
requestId: response.headers.get("x-request-id"),
55+
message: data.choices[0].message,
56+
};
57+
}
58+
59+
const requestId = response.headers.get("x-request-id");
5260
return {
53-
requestId: response.headers.get("x-request-id"),
54-
message: data.choices[0].message,
61+
requestId: requestId,
62+
message: {
63+
role: "Sssistant",
64+
content: `Sorry, an error occured with the chat completions API. (Status: ${response.status}, request ID: ${requestId})`,
65+
},
5566
};
5667
}
68+
69+
/** @type {import('..').GetFunctionCallsInterface} */
70+
export function getFunctionCalls(payload) {
71+
const functionCalls = payload.message.tool_calls;
72+
73+
if (!functionCalls) return [];
74+
75+
return functionCalls.map((call) => {
76+
return {
77+
id: call.id,
78+
function: {
79+
name: call.function.name,
80+
arguments: call.function.arguments,
81+
},
82+
};
83+
});
84+
}

‎test/prompt.test.js

+99-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { test, suite } from "node:test";
22

33
import { MockAgent } from "undici";
44

5-
import { prompt } from "../index.js";
5+
import { prompt, getFunctionCalls } from "../index.js";
66

77
suite("prompt", () => {
88
test("smoke", (t) => {
@@ -267,4 +267,102 @@ suite("prompt", () => {
267267
},
268268
});
269269
});
270+
271+
test("Handles error", async (t) => {
272+
const mockAgent = new MockAgent();
273+
function fetchMock(url, opts) {
274+
opts ||= {};
275+
opts.dispatcher = mockAgent;
276+
return fetch(url, opts);
277+
}
278+
279+
mockAgent.disableNetConnect();
280+
const mockPool = mockAgent.get("https://api.githubcopilot.com");
281+
mockPool
282+
.intercept({
283+
method: "post",
284+
path: `/chat/completions`,
285+
body: JSON.stringify({
286+
messages: [
287+
{
288+
role: "system",
289+
content: "You are a helpful assistant.",
290+
},
291+
{
292+
role: "user",
293+
content: "What is the capital of France?",
294+
},
295+
],
296+
model: "gpt-4",
297+
}),
298+
})
299+
.reply(400, "Bad Request", {
300+
headers: {
301+
"content-type": "text/plain",
302+
"x-request-id": "<request-id>",
303+
},
304+
});
305+
306+
const result = await prompt("What is the capital of France?", {
307+
token: "secret",
308+
model: "gpt-4",
309+
request: { fetch: fetchMock },
310+
});
311+
312+
t.assert.deepEqual(result, {
313+
message: {
314+
content:
315+
"Sorry, an error occured with the chat completions API. (Status: 400, request ID: <request-id>)",
316+
role: "Sssistant",
317+
},
318+
requestId: "<request-id>",
319+
});
320+
});
321+
322+
suite("getFunctionCalls()", () => {
323+
test("includes function calls", async (t) => {
324+
const tool_calls = [
325+
{
326+
function: {
327+
arguments: '{\n "order_id": "123"\n}',
328+
name: "get_delivery_date",
329+
},
330+
id: "call_Eko8Jz0mgchNOqiJJrrMr8YW",
331+
type: "function",
332+
},
333+
];
334+
const result = getFunctionCalls({
335+
requestId: "<request-id>",
336+
message: {
337+
role: "assistant",
338+
tool_calls,
339+
},
340+
});
341+
342+
t.assert.deepEqual(
343+
result,
344+
tool_calls.map((call) => {
345+
return {
346+
id: call.id,
347+
function: {
348+
name: call.function.name,
349+
arguments: call.function.arguments,
350+
},
351+
};
352+
})
353+
);
354+
});
355+
356+
test("does not include function calls", async (t) => {
357+
const result = getFunctionCalls({
358+
requestId: "<request-id>",
359+
message: {
360+
content: "Hello! How can I assist you today?",
361+
role: "assistant",
362+
},
363+
});
364+
365+
t.assert.deepEqual(result, []);
366+
});
367+
});
270368
});

0 commit comments

Comments
 (0)
Failed to load comments.