Skip to content

Commit

Permalink
add command to open notebook
Browse files Browse the repository at this point in the history
  • Loading branch information
pelikhan committed Jun 18, 2024
1 parent 083c22c commit 1992650
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 49 deletions.
10 changes: 8 additions & 2 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
"files": [
"./genaiscript.cjs",
"./icon-light.svg",
"./icon-dark.svg"
"./icon-dark.svg",
"./tutoria.md"
],
"contributes": {
"notebooks": [
Expand Down Expand Up @@ -227,6 +228,11 @@
}
],
"commands": [
{
"command": "genaiscript.notebook.create",
"title": "Create GenAIScript Mardown Notebook",
"category": "GenAIScript"
},
{
"command": "genaiscript.request.abort",
"title": "Abort OpenAI request",
Expand Down Expand Up @@ -361,4 +367,4 @@
"enabledApiProposals": [
"languageModels"
]
}
}
103 changes: 56 additions & 47 deletions packages/vscode/src/docsnotebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
parseKeyValuePairs,
parseBoolean,
} from "genaiscript-core"
import { Utils } from "vscode-uri"

// parser
// https://raw.githubusercontent.com/microsoft/vscode-markdown-notebook/main/src/markdownParser.ts
Expand Down Expand Up @@ -61,17 +62,6 @@ const NOTEBOOK_MARKERS: Record<
},
}

function clean(o: any) {
o = structuredClone(o)
Object.keys(o).forEach((k) => {
const v = o[k]
if (v === undefined) delete o[k]
if (Array.isArray(v) && v.length === 0) delete o[k]
else if (typeof v === "object" && JSON.stringify(v) == "{}") delete o[k]
})
return o
}

export async function activateDocsNotebook(state: ExtensionState) {
activateNotebookSerializer(state)
activateNotebookExecutor(state)
Expand Down Expand Up @@ -263,46 +253,48 @@ function activateNotebookSerializer(state: ExtensionState) {
const encoder = new TextEncoder()
const decoder = new TextDecoder()

const deserializeNotebook: (
data: Uint8Array,
token: vscode.CancellationToken
) => vscode.NotebookData = (data, token) => {
const content = decoder.decode(data)
const cellRawData = parseMarkdown(content)
const cells = cellRawData.map(
(data) =>
<vscode.NotebookCellData>{
kind: data.kind,
languageId: data.language,
metadata: {
leadingWhitespace: data.leadingWhitespace,
trailingWhitespace: data.trailingWhitespace,
options: data.options,
runnable: data.language === "javascript",
editable: true,
custom: true,
},
outputs: data.output
? [
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text(
data.output,
MARKDOWN_MIME_TYPE
),
]),
]
: [],
value: data.content,
}
)

const res = new vscode.NotebookData(cells)
return res
}

subscriptions.push(
vscode.workspace.registerNotebookSerializer(
NOTEBOOK_TYPE,
{
deserializeNotebook: (
data: Uint8Array,
token: vscode.CancellationToken
): vscode.NotebookData => {
const content = decoder.decode(data)
const cellRawData = parseMarkdown(content)
const cells = cellRawData.map(
(data) =>
<vscode.NotebookCellData>{
kind: data.kind,
languageId: data.language,
metadata: {
leadingWhitespace: data.leadingWhitespace,
trailingWhitespace: data.trailingWhitespace,
options: data.options,
runnable: data.language === "javascript",
editable: true,
custom: true,
},
outputs: data.output
? [
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text(
data.output,
MARKDOWN_MIME_TYPE
),
]),
]
: [],
value: data.content,
}
)

const res = new vscode.NotebookData(cells)
return res
},
deserializeNotebook,
serializeNotebook: function (
data: vscode.NotebookData,
token: vscode.CancellationToken
Expand Down Expand Up @@ -349,6 +341,23 @@ function activateNotebookSerializer(state: ExtensionState) {
{ transientOutputs: false }
)
)

subscriptions.push(
vscode.commands.registerCommand(
"genaiscript.notebook.create",
async (uri?: vscode.Uri) => {
uri = uri || Utils.joinPath(context.extensionUri, "tutorial.md")
const canceller = new vscode.CancellationTokenSource()
const bytes = await vscode.workspace.fs.readFile(uri)
const data = deserializeNotebook(bytes, canceller.token)
const notebook = await vscode.workspace.openNotebookDocument(
NOTEBOOK_TYPE,
data
)
vscode.window.showNotebookDocument(notebook)
}
)
)
}

interface RawNotebookCell {
Expand Down
191 changes: 191 additions & 0 deletions packages/vscode/tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
---
title: GenAIScript Tutorial
genaiscript:
files: "*.md"
model: openai:gpt-3.5-turbo
---

This Notebook is a GenAISCript tutorial. It is a Markdown document where each JavaScript code section is a runnable GenAIScript. You can execute each code block individually and see the results in the output section below the code block.



## Prompt as code

GenAIScript lets you write prompts as a JavaScript program. GenAIScript runs your program; generate chat messages; then handles the remaining interaction with the LLM API.

### `$`

Let's start with a simple hello world program.

```js
$`Say "hello!" in emojis`
```

<!-- genaiscript output start -->

<details>
<summary>👤 user</summary>

```markdown wrap
Say "hello!" in emojis
```

</details>

<details open>
<summary>🤖 assistant </summary>

```markdown wrap
👋😃!
```

</details>

<!-- genaiscript output end -->

The `$` function formats the strings and write them to the user message. This user message is added to the chat messages and sent to the LLM API. Under the snippet, you can review both the **user** message (that our program generated) and the **assistant** (LLM) response.

You can run the code block by clicking the **Execute Cell** button on the top left corner of the code block. It will be default try to use the `openai:gpt-3.5-turbo` LLM. If you need to use a different model, update the `model` field in the front matter at the start of the document. There are many options documented in [configuration](/genaiscript/getting-started/configuration).

Once the execution is done, you will also an additional **trace** entry that allows you to dive in the internal details of the GenAIScript execution. This is very helpful to diagnose issues with your prompts. The trace can be quite large so it is not serialized in the markdown file.

You can use the JavaScript `for` loop and sequence multiple `$` calls to append text to the user message. You can also inner expression to generate dynamic content.

```js
// let's give 3 tasks to the LLM
// to get 3 different outputs
for (let i = 1; i <= 3; i++) $`- Say "hello!" in ${i} emojis.`
$`Respond with a markdown list`
```

<!-- genaiscript output start -->

<details>
<summary>👤 user</summary>

```markdown wrap
- Say "hello!" in 1 emojis.
- Say "hello!" in 2 emojis.
- Say "hello!" in 3 emojis.
Respond with a markdown list
```

</details>

<details open>
<summary>🤖 assistant </summary>

```markdown wrap
- 👋
- 👋😊
- 👋✨😃
```

</details>

<!-- genaiscript output end -->

To recap, the GenAIScript runs and generates a user messages; that gets sent to the LLM. You can review the user message (and others) in the trace.

## `def` and `env.files`

The [`def` function](https://microsoft.github.io/genaiscript/reference/scripts/context/#definition-def) lets you declare and assign **LLM variables**. The concept of variable is most useful to import context data, in particular files, and refer to them in the rest of the prompt.

```js
def("FILE", env.files)
$`Summarize FILE in one short sentence. Respond as plain text.`
```

<!-- genaiscript output start -->

<details>
<summary>👤 user</summary>

``````markdown wrap
FILE:

```md file="src/samples/markdown.md"
---
title: What is Markdown? - Understanding Markdown Syntax
description: Learn about Markdown, a lightweight markup language for formatting plain text, its syntax, and how it differs from WYSIWYG editors.
keywords: Markdown, markup language, formatting, plain text, syntax
sidebar: mydoc_sidebar
---

What is Markdown?
Markdown is a lightweight markup language that you can use to add formatting elements to plaintext text documents. Created by John Gruber in 2004, Markdown is now one of the world’s most popular markup languages.

Using Markdown is different than using a WYSIWYG editor. In an application like Microsoft Word, you click buttons to format words and phrases, and the changes are visible immediately. Markdown isn’t like that. When you create a Markdown-formatted file, you add Markdown syntax to the text to indicate which words and phrases should look different.

For example, to denote a heading, you add a number sign before it (e.g., # Heading One). Or to make a phrase bold, you add two asterisks before and after it (e.g., **this text is bold**). It may take a while to get used to seeing Markdown syntax in your text, especially if you’re accustomed to WYSIWYG applications. The screenshot below shows a Markdown file displayed in the Visual Studio Code text editor....
```

Summarize FILE in one short sentence. Respond as plain text.
``````

</details>

<details open>
<summary>🤖 assistant </summary>

```markdown wrap
Markdown is a lightweight markup language for formatting plain text, using syntax to indicate formatting elements.
```

</details>

<!-- genaiscript output end -->

In GenAIScript, the [`env.files`](https://microsoft.github.io/genaiscript/reference/scripts/context/#environment-env) variable contains the [list of files in context](/genaiscript/reference/script/files), which can be determined by a user selection in the UI, CLI arguments, or pre-configured like in this script. You can change the files in `env.files` by editing the `files` field in the front matter at the start of the document.

### Filtering `env.files`

When using GenAIScript from the user interface, it is common to apply a script to an entire folder. This means that you'll get a bunch of files in `env.files` including some unneeded ones. The `def` function provides various options to filter the files, like the `endsWith` option.

`def` also provides `maxTokens` which will trim the content size to a number of tokens. LLM context is finite!

```js
script({ files: "src/**" }) // glob all files under src/samples
def("FILE", env.files, { endsWith: ".md", maxTokens: 1000 }) // only consider markdown files
$`Summarize FILE in one short sentence. Respond as plain text.`
```

<!-- genaiscript output start -->

<details>
<summary>👤 user</summary>

``````markdown wrap
FILE:

```md file="src/samples/markdown.md"
---
title: What is Markdown? - Understanding Markdown Syntax
description: Learn about Markdown, a lightweight markup language for formatting plain text, its syntax, and how it differs from WYSIWYG editors.
keywords: Markdown, markup language, formatting, plain text, syntax
sidebar: mydoc_sidebar
---

What is Markdown?
Markdown is a lightweight markup language that you can use to add formatting elements to plaintext text documents. Created by John Gruber in 2004, Markdown is now one of the world’s most popular markup languages.

Using Markdown is different than using a WYSIWYG editor. In an application like Microsoft Word, you click buttons to format words and phrases, and the changes are visible immediately. Markdown isn’t like that. When you create a Markdown-formatted file, you add Markdown syntax to the text to indicate which words and phrases should look different.

For example, to denote a heading, you add a number sign before it (e.g., # Heading One). Or to make a phrase bold, you add two asterisks before and after it (e.g., **this text is bold**). It may take a while to get used to seeing Markdown syntax in your text, especially if you’re accustomed to WYSIWYG applications. The screenshot below shows a Markdown file displayed in the Visual Studio Code text editor....
```

Summarize FILE in one short sentence. Respond as plain text.
``````

</details>

<details open>
<summary>🤖 assistant </summary>

```markdown wrap
Markdown is a lightweight markup language for formatting plaintext documents, different from WYSIWYG editors.
```

</details>

<!-- genaiscript output end -->

0 comments on commit 1992650

Please sign in to comment.