Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(examples): hello world, prompt #84

Merged
merged 36 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fc01ac5
Fixing typos
rajbos Sep 9, 2024
01315e3
Merge remote-tracking branch 'upstream/main'
rajbos Sep 13, 2024
97db5cc
More typos
rajbos Sep 13, 2024
54c7182
Added hello-world
rajbos Sep 13, 2024
b3cec6a
Adding handle prompt example
rajbos Sep 13, 2024
6b0300c
Adding handle prompt example
rajbos Sep 13, 2024
0d5663a
Merge branch 'main' into example
gr2m Sep 13, 2024
fbb3721
Trying to fix the note rendering
rajbos Sep 13, 2024
514fa22
Fix note
rajbos Sep 13, 2024
5d36cb4
Fix note
rajbos Sep 13, 2024
76493da
Update examples/1-hello-world/index.js
rajbos Sep 13, 2024
82fef70
Update examples/1-hello-world/package.json
rajbos Sep 13, 2024
e5f95f3
Update examples/1-hello-world/package.json
rajbos Sep 13, 2024
a91d03b
Update examples/1-hello-world/package.json
rajbos Sep 13, 2024
67160bc
update file to match the other example
rajbos Sep 13, 2024
7799456
Adding showing the user handle
rajbos Sep 13, 2024
a86354c
Stop tracking lock files
rajbos Sep 13, 2024
373f42a
Stop tracking lock files
rajbos Sep 13, 2024
7e058b9
Display url as well
rajbos Sep 13, 2024
01f1a0e
Remove unwanted change
rajbos Sep 13, 2024
037f7ab
Update .gitignore
rajbos Sep 13, 2024
a577333
Update examples/2-handle-prompt/index.js
rajbos Sep 13, 2024
6bc910d
Update examples/2-handle-prompt/index.js
rajbos Sep 13, 2024
0fcccbf
Handling review comments
rajbos Sep 14, 2024
d10e4ed
Adding payload verification
rajbos Sep 14, 2024
45ead22
Removed unneeded code
rajbos Sep 14, 2024
7057fd2
Merge branch 'main' into example
gr2m Sep 14, 2024
cc0ad34
remove number prefixes of examples folders
gr2m Sep 17, 2024
55a290b
style: prettier
gr2m Sep 17, 2024
7aba451
return after early response
gr2m Sep 17, 2024
f46d975
use actual body
gr2m Sep 17, 2024
1db7290
simplify code by using an async `getBody()` method, and use imported …
gr2m Sep 17, 2024
4f59a32
remove unnecessary new lines
gr2m Sep 17, 2024
dd6093a
more prompt example fixes based on testing
gr2m Sep 17, 2024
5c4aadf
moar fixes
gr2m Sep 17, 2024
6a0551d
simplify examples/README.md
gr2m Sep 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
coverage
examples/**/package-lock.json
node_modules
21 changes: 21 additions & 0 deletions examples/1-hello-world/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createServer } from "node:http";

import {
createTextEvent,
createDoneEvent,
} from "@copilot-extensions/preview-sdk";

const server = createServer((request, ressponse) => {
console.log(`Received [${request.method}] to [${request.url}]`);
if (request.method === "GET") {
return ressponse.end("ok");
}

ressponse.write(createTextEvent("Hello, world!"));
ressponse.end(createDoneEvent());
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
11 changes: 11 additions & 0 deletions examples/1-hello-world/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"private": true,
"type": "module",
"scripts": {
"start": "node index.js",
"watch": "node --watch index.js"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does node support a --watch flag now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woah today I learned, thanks!
https://nodejs.org/api/cli.html#--watch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot told me it was available!

},
"dependencies": {
"@copilot-extensions/preview-sdk": "latest"
}
}
111 changes: 111 additions & 0 deletions examples/2-handle-prompt/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { createServer } from 'node:http';
import { Octokit } from "@octokit";
import {
createAckEvent,
createDoneEvent,
createTextEvent,
parseRequestBody,
prompt,
verifyRequestByKeyId
} from "@copilot-extensions/preview-sdk";


// Define the handler function
const handler = async (request, response) => {

if (request.method !== 'POST') {
// Handle other request methods if necessary
response.writeHead(405, { 'Content-Type': 'text/plain' });
console.log(`Method ${request.method} not allowed`);

response.end('Method Not Allowed');
}

console.log('Received POST request');

// get a token to use
const tokenForUser = request.headers['x-github-token'];

// get the user information with the token
const octokit = new Octokit({ auth: tokenForUser });
const user = await octokit.request("GET /user");
const userHandle = user.data.login

// Collect incoming data chunks to use in the `on("end")` event
let body = '';
request.on('data', chunk => {
body += chunk.toString();
});

// Parse the collected data once the request ends
request.on('end', async () => {
try {
const payloadIsVerified = await verifyRequestByKeyId(
request.body,
signature,
keyId,
{
token: process.env.GITHUB_TOKEN,
},
);

if (!payloadIsVerified) {
console.error('Request verification failed');
response.writeHead(401, { 'Content-Type': 'text/plain' });
response.end('Request could not be verified');
return;
}

// start sending the response back to Copilot
console.log('Handling response');
// write the acknowledge event to let Copilot know we are handling the request
// this will also show the message "Copilot is responding" in the chat
response.write(createAckEvent());

// parse the incoming body as that has the information we need to handle the request / user prompt
const payload = parseRequestBody(body);

// start writing text in the response
const textEvent = createTextEvent(`Hello ${userHandle}, \n\n`);
response.write(textEvent);
// add new lines to mark the difference between the fixed text and the dynamic text
response.write("\n\n");

// get an authentication token to use
const tokenForUser = request.headers['x-github-token'];

console.log('Calling the GitHub Copilot API with the user prompt');
// the prompt to forward to the Copilot API is the last message in the payload
const payload_message = payload.messages[payload.messages.length - 1];
const result = await prompt(payload_message.content, {
system: "you talk like a pirate", // extra instructions for the prompt, the "you are a helpful assistant" is already set in the SDK
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "you are a helpful assistant" is just the default. If you want it to be part of your system prompt then you have to include it in your custom onew

Suggested change
system: "you talk like a pirate", // extra instructions for the prompt, the "you are a helpful assistant" is already set in the SDK
system: "You are a helpful assistant. You talk like a pirate",

messages: payload.messages, // we are giving the prompt the existing messages in this chat conversation
token: tokenForUser
});

// write the prompt response back to Copilot
// note that this is only send when the entire response from the Copilot API is ready
response.write(createTextEvent(result.message.content));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a prompt.stream API but we can do a separate example for that if you like?


// write the done event to let Copilot know we are done handling the request
response.end(createDoneEvent());
console.log('Response sent');
}
catch (error) {
console.error('Error:', error);
response.writeHead(500, { 'Content-Type': 'text/plain' });
response.end('Internal Server Error');
}
});
};

// Create an HTTP server
const server = http.createServer(handler);

// Define the port to listen on
const PORT = 3000;

// Start the server
server.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
12 changes: 12 additions & 0 deletions examples/2-handle-prompt/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"private": true,
"type": "module",
"scripts": {
"start": "node index.js",
"watch": "node --watch index.js"
},
"dependencies": {
"@copilot-extensions/preview-sdk": "latest",
"@octokit": "latest"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use latest for external dependencies.

Suggested change
"@octokit": "latest"
"octokit": "^4.0.2"

}
}
28 changes: 28 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
This folder contains examples to show how you can use the Copilot Agent SDK in your own projects.
Keep in mind that the SDK is in preview, so things might be changed already!

If you find any issues or have any questions, please let us know by opening an issue, pull requests welcome (please read the (contribution guidelines)[../CONTRIBUTING.md] first).

# Examples
Here's an overview of the examples in this folder:
- 1-hello-world: A simple example that responds to all prompts with a message: "Hello, world!"
- 2-handle-prompt: An example that responds to prompts with a message: "Hello, world!" and then forwards the prompt to the Copilot API. The response from the Copilot API is then sent back to the user.

## Running the examples
These examples are setup so that you can run them inside a GitHub Codespace. To do so, follow these steps:
1. Open the GitHub Codespace by clicking the "Code" button in the top right of the repository and selecting "Open with Codespaces".
2. Once the Codespace is open, you can run the examples by running the following command in the terminal:
```sh
# go to the correct folder
cd examples/<example-name>
# install dependencies
npm install
# run the example
npm run start
```

> [!IMPORTANT]
> By default, the examples will run on port 3000 and ports in Codespaces are private by default. If you want to access the examples from your browser, you'll need to make the port public. To do so, click on the "Ports" tab in the Codespace and right click, Go to "Port Visibility" next to port 3000 and set it to `public`. Do be aware that this will make the port accessible to anyone with the link to your Codespace. The port visibility setting is not saved when the Codespace is stopped, so you'll need to set it again if you restart the Codespace or node. The url does stay the same as long as the Codespace is not deleted.

> [!TIP]
> Don't forget to set the Codespace url (with the port) in your GitHub App that you are using. The Copilot backend needs to be able to reach your Codespace to send messages to your extension. If your app is not running, the port is not public, or the codespace is stopped, the extension will not work.
Loading