Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
55 changes: 54 additions & 1 deletion .github/scripts/setup-vercel-example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -423,10 +423,38 @@ liveblocks_key_value() {
ensure_liveblocks_public_key
jq -r '.publicKey.value // empty' <<<"${liveblocks_project}"
else
ensure_liveblocks_secret_key
jq -r '.secretKey.value // empty' <<<"${liveblocks_project}"
fi
}

ensure_liveblocks_secret_key() {
local liveblocks_secret_key_response
local secret_key_value

secret_key_value="$(jq -r '.secretKey.value // empty' <<<"${liveblocks_project}")"

[[ -n "${secret_key_value}" ]] && return

# Production secret keys are not returned at creation time, so roll one to obtain its value.
# Source: https://liveblocks.io/docs/api-reference/rest-api-endpoints#roll-project-secret-api-key
liveblocks_secret_key_response="$(
liveblocks_request \
"Rolling Liveblocks secret key" \
"POST" \
"${liveblocks_management_api_url}/projects/${liveblocks_project_id}/api-keys/secret/roll"
)"
secret_key_value="$(jq -r '.secretKey.value // empty' <<<"${liveblocks_secret_key_response}")"

if [[ -z "${secret_key_value}" ]]; then
err "Liveblocks secret key roll response did not include a value"
echo "${liveblocks_secret_key_response}" >&2
exit 1
fi

liveblocks_project="$(jq -c --arg value "${secret_key_value}" '.secretKey = { value: $value }' <<<"${liveblocks_project}")"
}

ensure_liveblocks_public_key() {
local liveblocks_project_response
local liveblocks_public_key_activated
Expand Down Expand Up @@ -481,6 +509,30 @@ add_liveblocks_key_to_vercel() {
vercel_liveblocks_env_name="${liveblocks_key_env_name}"
}

add_manual_env_vars_to_vercel() {
local manual_env_name

((${#manual_env_names[@]} == 0)) && return

# Any non-Liveblocks variable in `.env.example` is added as an empty placeholder
# for preview deployments. Their values must be filled in manually on Vercel later.
for manual_env_name in "${manual_env_names[@]}"; do
vercel_request \
"Adding placeholder env var ${manual_env_name} to Vercel preview" \
"POST" \
"$(vercel_url "/v10/projects/${vercel_project_id}/env")" \
"$(jq -n \
--arg key "${manual_env_name}" \
'{
key: $key,
value: "",
type: "encrypted",
target: ["preview"]
}')" \
>/dev/null
done
}

configure_vercel_project() {
local framework_json
local update_vercel_project_body
Expand Down Expand Up @@ -646,7 +698,7 @@ write_success_summary() {
fi
echo
if ((${#manual_env_names[@]} > 0)); then
echo "Still needs to be added manually on Vercel:"
echo "Added to Vercel for preview as empty placeholders (fill in their values manually):"
echo
for manual_env_name in "${manual_env_names[@]}"; do
echo "- \`${manual_env_name}\`"
Expand Down Expand Up @@ -680,6 +732,7 @@ main() {
create_vercel_project
create_liveblocks_project
add_liveblocks_key_to_vercel
add_manual_env_vars_to_vercel
configure_vercel_project
create_vercel_deploy_hook
add_vercel_custom_domain
Expand Down
5 changes: 5 additions & 0 deletions examples/nextjs-ai-elements-realtime/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# https://liveblocks.io/dashboard/apikeys
LIVEBLOCKS_SECRET_KEY=sk_xxx

# https://vercel.com/docs/ai-gateway
AI_GATEWAY_API_KEY=
12 changes: 12 additions & 0 deletions examples/nextjs-ai-elements-realtime/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.DS_Store
node_modules
.env
.env.*
!.env.example
*.tsbuildinfo
.vercel
.next
out
next-env.d.ts
# Turborepo
.turbo
101 changes: 101 additions & 0 deletions examples/nextjs-ai-elements-realtime/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<p align="center">
<a href="https://liveblocks.io#gh-light-mode-only">
<img src="https://raw.githubusercontent.com/liveblocks/liveblocks/main/.github/assets/header-light.svg" alt="Liveblocks" />
</a>
<a href="https://liveblocks.io#gh-dark-mode-only">
<img src="https://raw.githubusercontent.com/liveblocks/liveblocks/main/.github/assets/header-dark.svg" alt="Liveblocks" />
</a>
</p>

# Realtime AI chat with AI Elements

<p>
<a href="https://liveblocks.io/examples/nextjs-ai-elements-realtime">
<img src="https://img.shields.io/badge/live%20preview-message?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2Ljg0OSA0Ljc1SDBsNC44NDggNS4wNzV2Ny4wMDhsMTItMTIuMDgzWk03LjE1IDE5LjI1SDI0bC00Ljg0OS01LjA3NVY3LjE2N2wtMTIgMTIuMDgzWiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==&color=333" alt="Live Preview" />
</a>
<a href="https://codesandbox.io/s/github/liveblocks/liveblocks/tree/main/examples/nextjs-ai-elements-realtime">
<img src="https://img.shields.io/badge/open%20in%20codesandbox-message?style=flat&logo=codesandbox&color=333&logoColor=fff" alt="Open in CodeSandbox" />
</a>
<img src="https://img.shields.io/badge/react-message?style=flat&logo=react&color=0bd&logoColor=fff" alt="React" />
<img src="https://img.shields.io/badge/next.js-message?style=flat&logo=next.js&color=07f&logoColor=fff" alt="Next.js" />
</p>

This example shows how to build a realtime, multiplayer AI chat with
[Liveblocks Feeds](https://liveblocks.io/docs/collaboration-features/ai-collaboration),
[Next.js](https://nextjs.org/), and [AI Elements](https://ai-sdk.dev/elements).

Each chat is a feed inside a Liveblocks room, so messages sync instantly to
everyone connected, complete with shared presence (avatar stack and a live "AI
is thinking…" status). The AI reply is generated on the server and streamed back
into the feed with `@liveblocks/node` (`createFeedMessage` + `updateFeedMessage`),
then rendered live for all users through the `useFeedMessages` hook — including
reasoning, chain of thought, tool calls, sources, and token usage via AI
Elements.

## Getting started

Run the following command to try this example locally:

```bash
npx create-liveblocks-app@latest --example nextjs-ai-elements-realtime --api-key
```

This will download the example and ask permission to open your browser, enabling
you to automatically get your API key from your
[liveblocks.io](https://liveblocks.io) account.

### Manual setup

<details><summary>Read more</summary>

<p></p>

Alternatively, you can set up your project manually:

- Install all dependencies with `npm install`
- Create an account on [liveblocks.io](https://liveblocks.io/dashboard)
- Copy your **secret** key from the
[dashboard](https://liveblocks.io/dashboard/apikeys)
- Create an `.env.local` file and add your **secret** key as the
`LIVEBLOCKS_SECRET_KEY` environment variable
- _(Optional)_ Add an `AI_GATEWAY_API_KEY` from the
[Vercel AI Gateway](https://vercel.com/docs/ai-gateway) to get real model
responses. Without it, the example uses a built-in mock assistant so it still
runs end to end.
- Run `npm run dev` and go to [http://localhost:3000](http://localhost:3000)

To see the realtime sync, open the page in two browser tabs and send a message
from one — it appears instantly in both, along with the AI's reply.

</details>

### Deploy on Vercel

<details><summary>Read more</summary>

<p></p>

To both deploy on [Vercel](https://vercel.com), and run the example locally, use
the following command:

```bash
npx create-liveblocks-app@latest --example nextjs-ai-elements-realtime --vercel
```

This will download the example and ask permission to open your browser, enabling
you to deploy to Vercel.

</details>

### Develop on CodeSandbox

<details><summary>Read more</summary>

<p></p>

After forking
[this example](https://codesandbox.io/s/github/liveblocks/liveblocks/tree/main/examples/nextjs-ai-elements-realtime)
on CodeSandbox, create the `LIVEBLOCKS_SECRET_KEY` environment variable as a
[secret](https://codesandbox.io/docs/secrets).

</details>
Loading
Loading