Skip to content

Commit

Permalink
Use collapsible markdown widgets for cells in srcmd (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjreinhart authored Jul 17, 2024
1 parent d4c191c commit 74b1e81
Show file tree
Hide file tree
Showing 12 changed files with 566 additions and 442 deletions.
21 changes: 16 additions & 5 deletions packages/api/srcbook/examples/getting-started.srcmd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

# Getting started

###### package.json
<details>
<summary>package.json</summary>

```json
{
Expand All @@ -12,6 +13,7 @@
}
}
```
</details>

## What are Srcbooks?

Expand All @@ -24,51 +26,60 @@ A Srcbook is composed of **cells**. Currently, there are 4 types of cells:
3. **markdown cell**: what you're reading is a markdown cell. It allows you to easily express ideas with rich markup, rather than code comments, an idea called [literate programming](https://en.wikipedia.org/wiki/Literate_programming).
4. **code cell**: think of these as JS or TS files. You can run them or export objects to be used in other cells.

###### simple-code.js
<details open>
<summary>simple-code.js</summary>

```javascript
// This is a trivial code cell. You can run me by
// clicking 'Run' or using the shortcut `cmd` + `enter`.
console.log("Hello, Srcbook!")
```
</details>

## Dependencies

You can add any external node.js-compatible dependency from [npm](https://www.npmjs.com/). Let's look at an example below by importing the `random-words` library.

You'll need to make sure you install dependencies, which you can do by running the `package.json` cell above.

###### generate-random-word.js
<details open>
<summary>generate-random-word.js</summary>

```javascript
import {generate} from 'random-words';

console.log(generate())
```
</details>

## Importing other cells

Behind the scenes, cells are files of JavaScript or TypeScript code. They are ECMAScript 6 modules. Therefore you can export variables from one file and import them in another.

###### star-wars.js
<details open>
<summary>star-wars.js</summary>

```javascript
export const func = (name) => `I am your father, ${name}`
```
</details>

###### logger.js
<details open>
<summary>logger.js</summary>

```javascript
import {func} from './star-wars.js';

console.log(func("Luke"));
```
</details>

## Using secrets

For security purposes, you should avoid pasting secrets directly into Srcbooks. The mechanism you should leverage is [secrets](/secrets). These are stored securely and are accessed at runtime as environment variables.

Secrets can then be imported in Srcbooks using `process.env.SECRET_NAME`:

```
const API_KEY = process.env.SECRET_API_KEY;
const token = auth(API_KEY);
Expand Down
26 changes: 18 additions & 8 deletions packages/api/srcbook/examples/langgraph-web-agent.srcmd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

# LangGraph web agent

###### package.json
<details>
<summary>package.json</summary>

```json
{
Expand All @@ -19,6 +20,7 @@
}
}
```
</details>

## LangGraph tutorial

Expand All @@ -28,20 +30,23 @@ We're going to build an agent that can search the web using the [Tavily Search A

First, let's ensure we've setup the right env variables:

###### env-check.ts
<details open>
<summary>env-check.ts</summary>

```typescript
import assert from 'node:assert';

assert.ok(process.env.OPENAI_API_KEY, 'You need to set OPENAI_API_KEY');
assert.ok(process.env.TAVILY_API_KEY, 'You need to set TAVILY_API_KEY');
```
</details>

## Define the agent

Now, let's define the Agent with LangGraph.js

###### agent.ts
<details open>
<summary>agent.ts</summary>

```typescript
import { HumanMessage } from "@langchain/core/messages";
Expand Down Expand Up @@ -112,12 +117,13 @@ export const memory = SqliteSaver.fromConnString(DB_NAME);
// This compiles it into a LangChain Runnable.
// Note that we're (optionally) passing the memory when compiling the graph
export const app = workflow.compile({ checkpointer: memory });

```
</details>

Now that we've built our app, let's invoke it to first get the weather in SF:

###### sf-weather.ts
<details open>
<summary>sf-weather.ts</summary>

```typescript
import {app} from './agent.ts';
Expand All @@ -134,12 +140,14 @@ const finalState = await app.invoke(

console.log(finalState.messages[finalState.messages.length - 1].content)
```
</details>

Now when we pass the same `thread_id`, in this case `"42"`, the conversation context is retained via the saved state that we've set in a local sqliteDB (i.e. stored list of messages).

Also, in this next example, we demonstrate streaming output.

###### ny-weather.ts
<details open>
<summary>ny-weather.ts</summary>

```typescript
import {app} from './agent.ts';
Expand All @@ -152,17 +160,19 @@ const nextState = await app.invoke(

console.log(nextState.messages[nextState.messages.length - 1].content);
```
</details>

## Clear memory

The memory was saved in the sqlite db `./langGraph.db`. If you want to clear it, run the following cell

###### clear.ts
<details open>
<summary>clear.ts</summary>

```typescript
import {DB_NAME} from './agent.ts';
import fs from 'node:fs';
// I can't find good documentation on the memory module, so let's apply the nuclear method

fs.rmSync(DB_NAME);
```
</details>
21 changes: 15 additions & 6 deletions packages/api/srcbook/examples/websockets.srcmd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

# Intro to WebSockets

###### package.json
<details>
<summary>package.json</summary>

```json
{
Expand All @@ -12,6 +13,7 @@
}
}
```
</details>

This Srcbook is a fun demonstration of building a simple WebSocket client and server in Node.js using the [ws library](https://github.com/websockets/ws). We'll explore the basic concepts of communicating over WebSockets and showcase Srcbook's ability to host long-running processes.

Expand All @@ -31,7 +33,8 @@ One of the most popular libraries for WebSockets in Node.js is the [ws library](

Below we implement a simple WebSocket _server_ using `ws`.

###### simple-server.js
<details open>
<summary>simple-server.js</summary>

```javascript
import { WebSocketServer } from 'ws';
Expand All @@ -46,12 +49,14 @@ wss.on('connection', (socket) => {
console.log("New client connected")
});
```
</details>

This simple server does nothing more than wait for incoming connections and log the messages it receives.

Next, we need a _client_ to connect and send messages to it. Note the client is running in a Node.js process, not in the browser. Backends communicate over WebSockets too!

###### simple-client.js
<details open>
<summary>simple-client.js</summary>

```javascript
import WebSocket from 'ws';
Expand All @@ -63,16 +68,17 @@ ws.on('open', () => {
ws.send('Hello from simple-client.js');
ws.close();
});

```
</details>

Our simple client establishes a connection with the server, sends one message, and closes the connection. To run this example, first run the server and then run the client. Output is logged under the simple-server.js cell above.

## Stateful connections

The example above is not terribly interesting. WebSockets become more useful when the server tracks open connections and sends messages to the client.

###### stateful-server.js
<details open>
<summary>stateful-server.js</summary>

```javascript
import { WebSocketServer } from 'ws';
Expand Down Expand Up @@ -132,8 +138,10 @@ wss.on('connection', (socket) => {
});
});
```
</details>

###### client.js
<details open>
<summary>client.js</summary>

```javascript
import WebSocket from 'ws';
Expand Down Expand Up @@ -175,6 +183,7 @@ client2.close();

console.log("Shutting down");
```
</details>

## Explanation

Expand Down
Loading

0 comments on commit 74b1e81

Please sign in to comment.