Skip to content
Draft
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
28 changes: 28 additions & 0 deletions .github/workflows/llm_chatbot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: llm_chatbot

on:
push:
branches: [master]
pull_request:
paths:
- rust/llm_chatbot/**
- .github/workflows/llm_chatbot.yml

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
rust-llm_chatbot:
runs-on: ubuntu-24.04
container: ghcr.io/dfinity/icp-dev-env-rust:1.0.1
env:
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy and test
working-directory: rust/llm_chatbot
run: |
icp network start -d
icp deploy
bash test.sh
20 changes: 0 additions & 20 deletions rust/llm_chatbot/.devcontainer/devcontainer.json

This file was deleted.

113 changes: 0 additions & 113 deletions rust/llm_chatbot/BUILD.md

This file was deleted.

69 changes: 46 additions & 23 deletions rust/llm_chatbot/README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,73 @@
# LLM Chatbot

The LLM Chatbot example demonstrates how an ICP smart contract can be used to interact with a large language model (LLM) to generate text. The user can input a prompt, and the smart contract will use the LLM to generate a response.
The response is then returned to the user, and the user can submit some follow-up prompts to continue the conversation.
This example demonstrates how an ICP smart contract can interact with a large language model (LLM) to generate text. The user can input a prompt and the smart contract will use the LLM to generate a response. Follow-up prompts continue the conversation with the full message history.

## Deploying from ICP Ninja
## How it works

When viewing this project in ICP Ninja, you can deploy it directly to the mainnet for free by clicking "Run" in the upper right corner. Open this project in ICP Ninja:
The backend canister calls the `w36hm-eqaaa-aaaal-qr76a-cai` LLM canister on mainnet via the [`ic-llm`](https://crates.io/crates/ic-llm) crate. Locally, `icp deploy` deploys a copy of the LLM canister (backed by Ollama) and automatically injects its canister ID into the backend as the `PUBLIC_CANISTER_ID:llm` environment variable — `ic-llm` reads this at runtime so local calls go to the local LLM. On mainnet, the env var is absent and `ic-llm` falls back to the hardcoded mainnet principal.

[![](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/rust/llm_chatbot)
## Build and deploy from the command line

## Deploying from ICP Ninja
### Prerequisites

[![](https://icp.ninja/assets/open.svg)](https://icp.ninja/editor?g=https://github.com/dfinity/examples/tree/master/rust/counter)
- Node.js
- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
- ic-mops: `npm install -g ic-mops`

## Build and deploy from the command-line
### Set up Ollama (local deployment only)

### 1. [Download and install the IC SDK.](https://internetcomputer.org/docs/building-apps/getting-started/install)
The LLM canister delegates inference to [Ollama](https://ollama.com/). Install it and then run:

### 2. Download your project from ICP Ninja using the 'Download files' button on the upper left corner, or [clone the GitHub examples repository.](https://github.com/dfinity/examples/)
```bash
ollama serve
```

In a separate terminal, pull the model (about 4 GiB, one-time download):

### 3. Navigate into the project's directory.
```bash
ollama pull llama3.1:8b
```

### 4. Set up Ollama
Once the model is loaded you can stop the `ollama run` command — `ollama serve` keeps it available.

To be able to test the agent locally, you'll need a server for processing the agent's prompts. For that, we'll use `ollama`, which is a tool that can download and serve LLMs.
See the documentation on the [Ollama website](https://ollama.com/) to install it. Once it's installed, run:
### Install

```bash
git clone https://github.com/dfinity/examples
cd examples/rust/llm_chatbot
```
ollama serve
# Expected to start listening on port 11434

### Deploy and test

```bash
icp network start -d
icp deploy
bash test.sh
icp network stop
```

The above command will start the Ollama server, so that it can process requests by the agent. Additionally, and in a separate window, run the following command to download the LLM that will be used by the agent:
Open the frontend URL printed by `icp deploy` to use the chatbot in the browser.

For hot-reload frontend development:

```bash
npm run dev --prefix frontend
```
ollama run llama3.1:8b

## Deploying to mainnet

```bash
icp deploy -e ic
```

The above command will download an 8B parameter model, which is around 4GiB. Once the command executes and the model is loaded, you can terminate it. You won't need to do this step again.
No Ollama setup is needed — mainnet calls go directly to the LLM canister at `w36hm-eqaaa-aaaal-qr76a-cai`.

### 5. Deploy the project to your local environment:
## Updating the Candid interface

```
dfx start --background --clean && dfx deploy
```bash
icp build backend && candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.did
```

## Security considerations and best practices

If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices.
If you base your application on this example, familiarize yourself with the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for developing on ICP. This example may not implement all best practices.
10 changes: 4 additions & 6 deletions rust/llm_chatbot/backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
[package]
name = "backend"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
edition = "2024"

[lib]
crate-type = ["cdylib"]
path = "lib.rs"

[dependencies]
candid = "0.10.13"
ic-cdk = "0.17.1"
ic-llm = "1.1.0"
candid = "0.10"
ic-cdk = "0.20"
ic-llm = "1.2.0"
17 changes: 17 additions & 0 deletions rust/llm_chatbot/backend/backend.did
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
type AssistantMessage = record {
content : opt text;
tool_calls : vec ToolCall;
};
type ChatMessage = variant {
tool : record { content : text; tool_call_id : text };
user : record { content : text };
assistant : AssistantMessage;
system : record { content : text };
};
type FunctionCall = record { name : text; arguments : vec ToolCallArgument };
type ToolCall = record { id : text; function : FunctionCall };
type ToolCallArgument = record { value : text; name : text };
service : {
chat : (vec ChatMessage) -> (text);
prompt : (text) -> (text);
}
40 changes: 0 additions & 40 deletions rust/llm_chatbot/dfx.json

This file was deleted.

9 changes: 3 additions & 6 deletions rust/llm_chatbot/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,21 @@
"private": true,
"type": "module",
"scripts": {
"prebuild": "npm i --include=dev && dfx generate backend",
"prebuild": "npm i --include=dev",
"build": "vite build",
"dev": "vite"
},
"dependencies": {
"@icp-sdk/core": "~5.2.0",
"react-json-view-lite": "2.3.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@icp-sdk/bindgen": "~0.2.2",
"@vitejs/plugin-react": "4.3.3",
"autoprefixer": "^10.4.20",
"postcss": "8.4.48",
"tailwindcss": "3.4.14",
"vite": "5.4.11",
"vite-plugin-environment": "1.1.3"
"vite": "5.4.11"
}
}
22 changes: 22 additions & 0 deletions rust/llm_chatbot/frontend/src/actor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env";
import { createActor } from "./bindings/backend";

// The ic_env cookie is set by the asset canister on all HTML responses. It
// contains the replica root key and PUBLIC_* canister environment variables.
// In dev mode the Vite dev server sets the same cookie (see vite.config.js).
const canisterEnv = safeGetCanisterEnv();

const canisterId = canisterEnv?.["PUBLIC_CANISTER_ID:backend"];

if (!canisterId) {
throw new Error(
"Canister ID for 'backend' not found. Run 'icp deploy' first."
);
}

export const backend = createActor(canisterId, {
agentOptions: {
host: window.location.origin,
rootKey: canisterEnv?.IC_ROOT_KEY,
},
});
2 changes: 1 addition & 1 deletion rust/llm_chatbot/frontend/src/main.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom/client';
import { backend } from 'declarations/backend';
import { backend } from './actor';
import botImg from '/bot.svg';
import userImg from '/user.svg';
import '/index.css';
Expand Down
Loading
Loading