├── .changeset
├── README.md
└── config.json
├── .eslintrc.js
├── .github
├── ISSUE_TEMPLATE
│ ├── 1.bug_report.yml
│ ├── 2.feature_request.yml
│ └── config.yml
├── SECURITY.md
├── dependabot.yml
├── playwright.yml
├── scripts
│ ├── cleanup-examples-changesets.mjs
│ ├── release-snapshot.js
│ └── update-examples.ts
└── workflows
│ ├── ci.yml
│ ├── quality.yml
│ ├── release-snapshot.yml
│ ├── release.yml
│ └── rv.yml
├── .gitignore
├── .husky
└── pre-commit
├── .kodiak.toml
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .vscode
├── settings.json
└── spellright.dict
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── examples
├── nextjs
│ ├── .env.local.example
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── app
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── langbase
│ │ │ └── pipe
│ │ │ │ ├── run-stream
│ │ │ │ └── route.ts
│ │ │ │ └── run
│ │ │ │ └── route.ts
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── components.json
│ ├── components
│ │ ├── langbase
│ │ │ ├── chat-advanced.tsx
│ │ │ ├── generate-text.tsx
│ │ │ ├── run-stream.tsx
│ │ │ ├── run.tsx
│ │ │ └── stream-text.tsx
│ │ └── ui
│ │ │ ├── button.tsx
│ │ │ └── input.tsx
│ ├── lib
│ │ └── utils.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── tailwind.config.ts
│ └── tsconfig.json
└── nodejs
│ ├── .env.example
│ ├── .gitignore
│ ├── .prettierrc
│ ├── chunker
│ └── index.ts
│ ├── embed
│ └── index.ts
│ ├── memory
│ ├── memory.create.ts
│ ├── memory.delete.ts
│ ├── memory.docs.delete.ts
│ ├── memory.docs.list.ts
│ ├── memory.docs.retry-embed.ts
│ ├── memory.docs.upload.ts
│ ├── memory.list.ts
│ ├── memory.retrieve.filters.And.ts
│ ├── memory.retrieve.filters.Eq.ts
│ ├── memory.retrieve.filters.In.ts
│ ├── memory.retrieve.filters.NotEq.ts
│ ├── memory.retrieve.filters.NotIn.ts
│ ├── memory.retrieve.filters.advanced.ts
│ ├── memory.retrieve.ts
│ └── multi-agent-memory-routing.ts
│ ├── package.json
│ ├── parser
│ ├── composable-ai.md
│ └── index.ts
│ ├── pipes
│ ├── multi-agent.ts
│ ├── pipe.create.ts
│ ├── pipe.list.ts
│ ├── pipe.run.chat.ts
│ ├── pipe.run.pipe.key.ts
│ ├── pipe.run.stream.chat.ts
│ ├── pipe.run.stream.llmkey.ts
│ ├── pipe.run.stream.ts
│ ├── pipe.run.ts
│ ├── pipe.structured.outputs.ts
│ ├── pipe.tool.stream.ts
│ ├── pipe.tool.ts
│ └── pipe.update.ts
│ ├── readme.md
│ ├── threads
│ ├── threads.append.ts
│ ├── threads.create.ts
│ ├── threads.delete.ts
│ ├── threads.get.ts
│ ├── threads.messages.list.ts
│ └── threads.update.ts
│ ├── tools
│ ├── crawl.ts
│ └── web-search.ts
│ └── workflows
│ └── workflows.ts
├── package.json
├── packages
├── cli
│ ├── .gitignore
│ ├── .prettierrc.json
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── README.md
│ ├── bin
│ │ └── langbase.js
│ ├── package.json
│ ├── src
│ │ ├── auth
│ │ │ └── index.ts
│ │ ├── build
│ │ │ └── index.ts
│ │ ├── data
│ │ │ ├── constants.ts
│ │ │ ├── index.ts
│ │ │ └── models.ts
│ │ ├── deploy
│ │ │ └── index.ts
│ │ ├── docs-mcp-server
│ │ │ ├── docs.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── utils
│ │ │ ├── build.ts
│ │ │ ├── cli.ts
│ │ │ ├── common
│ │ │ ├── errors.ts
│ │ │ ├── request.ts
│ │ │ └── stream.ts
│ │ │ ├── debug-mode.ts
│ │ │ ├── find-env.ts
│ │ │ ├── formatting.ts
│ │ │ ├── get-base-url.ts
│ │ │ ├── get-score.ts
│ │ │ ├── handle-error.ts
│ │ │ ├── heading.ts
│ │ │ ├── init.ts
│ │ │ ├── retrieve-credentials.ts
│ │ │ └── schema.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ └── types
│ │ └── index.ts
└── langbase
│ ├── .env.example
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── playwright.config.ts
│ ├── readme.md
│ ├── src
│ ├── common
│ │ ├── errors.ts
│ │ ├── request.ts
│ │ └── stream.ts
│ ├── data
│ │ └── constants.ts
│ ├── examples
│ │ └── pipes
│ │ │ ├── generate-text.ts
│ │ │ └── stream-text.ts
│ ├── index.ts
│ ├── langbase
│ │ ├── langbase.ts
│ │ └── workflows.ts
│ ├── lib
│ │ ├── helpers
│ │ │ └── index.ts
│ │ └── utils
│ │ │ └── doc-to-formdata.ts
│ ├── pipes
│ │ ├── pipes.test.ts
│ │ └── pipes.ts
│ └── react
│ │ ├── index.ts
│ │ ├── use-pipe.ts
│ │ └── use-pipe.ui.test.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── turbo.json
│ ├── types
│ └── index.ts
│ ├── vitest.edge.config.js
│ ├── vitest.node.config.js
│ └── vitest.ui.react.config.js
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── tools
├── eslint-config
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── index.js
│ ├── library.js
│ ├── next.js
│ ├── package.json
│ └── react-internal.js
└── tsconfig
│ ├── CHANGELOG.md
│ ├── base.json
│ ├── nextjs.json
│ ├── node14.json
│ ├── package.json
│ └── react-library.json
└── turbo.json
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "fixed": [],
6 | "linked": [],
7 | "access": "public",
8 | "baseBranch": "main",
9 | "updateInternalDependencies": "patch"
10 | }
11 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ['@langbase/eslint-config/library.js'],
4 | settings: {
5 | next: {
6 | rootDir: ['apps/*/'],
7 | },
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/1.bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug report
2 | description: Report a bug for Langbase.
3 | labels: []
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | This template is to report bugs for the Langbase. If you need help with your own project, feel free to [start a new thread in our discussions](https://github.com/LangbaseInc/langbase/discussions).
9 | - type: textarea
10 | attributes:
11 | label: Description
12 | description: A detailed bug description for Langbase and steps to reproduce it. Include the API, framework, and AI provider you're using.
13 | placeholder: |
14 | Steps to reproduce...
15 | validations:
16 | required: true
17 | - type: textarea
18 | attributes:
19 | label: Code example
20 | description: Provide an example code snippet that may have a problem
21 | placeholder: |
22 | ...
23 | - type: textarea
24 | attributes:
25 | label: Additional context
26 | description: |
27 | Any additional information that might help us investigate.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/2.feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Propose a new feature for Langbase.
3 | labels: []
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | Use this template to propose new features for Langbase. If you need help with your project, [start a new thread in our discussions](https://github.com/LangbaseInc/langbase/discussions).
9 | - type: textarea
10 | attributes:
11 | label: Feature Description
12 | description: Describe the feature you are proposing. Include the API, framework, and AI provider.
13 | placeholder: Feature description...
14 | validations:
15 | required: true
16 | - type: textarea
17 | attributes:
18 | label: Use Case
19 | description: Explain how this feature would be beneficial.
20 | placeholder: Use case...
21 | - type: textarea
22 | attributes:
23 | label: Additional Context
24 | description: Any additional information that might help us understand your request.
25 | placeholder: Additional context...
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Ask a Question
4 | url: https://github.com/LangbaseInc/langbase/discussions
5 | about: Please ask your questions in our discussions forum.
6 |
--------------------------------------------------------------------------------
/.github/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting Security Issues
2 |
3 | If you find a security vulnerability, please email us at `security@langbase.com`.
4 |
5 | We will promptly investigate and fix legitimate reports.
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: 'npm'
4 | directory: '/'
5 | schedule:
6 | interval: 'daily'
7 |
--------------------------------------------------------------------------------
/.github/playwright.yml:
--------------------------------------------------------------------------------
1 | name: Playwright Tests
2 | on:
3 | push:
4 | branches: [ main, master ]
5 | pull_request:
6 | branches: [ main, master ]
7 | jobs:
8 | test:
9 | timeout-minutes: 60
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: actions/setup-node@v4
14 | with:
15 | node-version: lts/*
16 | - name: Install dependencies
17 | run: npm install -g pnpm && pnpm install
18 | - name: Install Playwright Browsers
19 | run: pnpm exec playwright install --with-deps
20 | - name: Run Playwright tests
21 | run: pnpm exec playwright test
22 | - uses: actions/upload-artifact@v4
23 | if: always()
24 | with:
25 | name: playwright-report
26 | path: playwright-report/
27 | retention-days: 30
28 |
--------------------------------------------------------------------------------
/.github/scripts/cleanup-examples-changesets.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * `changeset version` updates the version and adds a changelog file in
3 | * the example apps, but we don't want to do that. So this script reverts
4 | * any "version" field changes and deletes the `CHANGELOG.md` file.
5 | *
6 | * Source: https://github.com/TooTallNate/nx.js/blob/main/.github/scripts/cleanup-examples.mjs
7 | */
8 |
9 | import {
10 | readFileSync,
11 | readdirSync,
12 | statSync,
13 | unlinkSync,
14 | writeFileSync,
15 | } from 'node:fs';
16 | import {join} from 'path';
17 | import {fileURLToPath} from 'url';
18 |
19 | const examplesUrl = new URL('../../examples', import.meta.url);
20 | const examplesDir = fileURLToPath(examplesUrl);
21 |
22 | console.log('Cleaning up examples...', examplesDir);
23 |
24 | for (const app of readdirSync(examplesDir)) {
25 | const appPath = join(examplesDir, app);
26 | if (statSync(appPath).isDirectory()) {
27 | const packageJsonPath = join(appPath, 'package.json');
28 | const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
29 | packageJson.version = '0.0.0';
30 | writeFileSync(
31 | packageJsonPath,
32 | JSON.stringify(packageJson, null, 2) + '\n',
33 | );
34 |
35 | try {
36 | const changelogUrl = new URL(
37 | `examples/${app}/CHANGELOG.md`,
38 | examplesUrl,
39 | );
40 | console.log('Deleting', changelogUrl.href);
41 | unlinkSync(changelogUrl);
42 | } catch (err) {
43 | if (err.code !== 'ENOENT') throw err;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/.github/scripts/release-snapshot.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This script creates a snapshot release by performing the following steps:
3 | * 1. Ensures the script is running from the project root directory.
4 | * 2. Defines a function to execute shell commands and log their output.
5 | * 3. Defines a function to update the version in a given package.json file.
6 | * - If the current version is already a snapshot, it increments the snapshot number.
7 | * - If the current version is not a snapshot, it increments the patch version and sets the snapshot number to 0.
8 | * 4. Retrieves the current commit short SHA.
9 | * 5. Bumps the version in the specified package.json files.
10 | * 6. Runs a series of commands to version, build, and publish the packages as a snapshot release.
11 | *
12 | * @requires child_process
13 | * @requires path
14 | * @requires fs
15 | */
16 | const {execSync} = require('child_process');
17 | const path = require('path');
18 | const fs = require('fs');
19 |
20 | // Ensure we're in the project root
21 | process.chdir(path.resolve(__dirname, '../..'));
22 |
23 | // Function to execute commands and log output
24 | function run(command) {
25 | console.log(`Running: ${command}`);
26 | try {
27 | execSync(command, {stdio: 'inherit'});
28 | } catch (error) {
29 | console.error(`Error executing command: ${command}`);
30 | console.error(error);
31 | process.exit(1);
32 | }
33 | }
34 |
35 | // Function to update version in package.json
36 | function bumpVersion(packagePath) {
37 | const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
38 | const currentVersion = pkg.version;
39 | let [major, minor, patch, snapshot] = currentVersion
40 | .split(/[-.]/)
41 | .map(v => (isNaN(parseInt(v)) ? v : parseInt(v)));
42 |
43 | if (snapshot === 'snapshot') {
44 | // If already a snapshot, increment the snapshot number
45 | snapshot = parseInt(pkg.version.split('-snapshot.')[1]) + 1;
46 | } else {
47 | // If not a snapshot, increment patch and set snapshot to 0
48 | patch += 1;
49 | snapshot = 0;
50 | }
51 |
52 | pkg.version = `${major}.${minor}.${patch}-snapshot.${snapshot}`;
53 | fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2));
54 | console.log(`Updated ${packagePath} to version ${pkg.version}`);
55 | }
56 |
57 | // Get the current commit short SHA
58 | const SHORT_SHA = execSync('git rev-parse --short HEAD').toString().trim();
59 |
60 | console.log('Creating snapshot release...');
61 |
62 | // Bump versions
63 | bumpVersion('./packages/langbase/package.json');
64 |
65 | // Version and tag the snapshot release
66 | run(`pnpm changeset version --snapshot ${SHORT_SHA}`);
67 |
68 | // Build and publish the snapshot release
69 | run('pnpm build:pkgs');
70 | run('pnpm changeset publish --no-git-tag --tag snapshot');
71 |
72 | // Reset Git changes
73 | console.log('Git commit and push changes...');
74 |
75 | console.log('All changes have been reset. Snapshot release process complete!');
76 |
--------------------------------------------------------------------------------
/.github/scripts/update-examples.ts:
--------------------------------------------------------------------------------
1 | import {execSync} from 'child_process';
2 | import fs from 'fs';
3 | import path from 'path';
4 | import {fileURLToPath} from 'url';
5 |
6 | const __filename = fileURLToPath(import.meta.url);
7 | const __dirname = path.dirname(__filename);
8 |
9 | const examplesDir = path.join(__dirname, '..', '..', 'examples');
10 |
11 | function installDependencies(packageDir) {
12 | console.log(`Installing dependencies in ${packageDir}`);
13 | try {
14 | execSync('pnpm i langbase@latest', {
15 | cwd: packageDir,
16 | stdio: 'inherit',
17 | });
18 | console.log(`Successfully installed dependencies in ${packageDir}`);
19 | } catch (error) {
20 | console.error(
21 | `Error installing dependencies in ${packageDir}:`,
22 | error.message,
23 | );
24 | }
25 | }
26 |
27 | function processExamples() {
28 | if (!fs.existsSync(examplesDir)) {
29 | console.error('Examples directory not found');
30 | return;
31 | }
32 |
33 | const entries = fs.readdirSync(examplesDir, {withFileTypes: true});
34 |
35 | for (const entry of entries) {
36 | if (entry.isDirectory()) {
37 | const packageDir = path.join(examplesDir, entry.name);
38 | const packageJsonPath = path.join(packageDir, 'package.json');
39 |
40 | if (fs.existsSync(packageJsonPath)) {
41 | installDependencies(packageDir);
42 | } else {
43 | console.log(`Skipping ${entry.name}: No package.json found`);
44 | }
45 | }
46 | }
47 | }
48 |
49 | processExamples();
50 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | jobs:
10 | test:
11 | name: 'Test'
12 | runs-on: ubuntu-latest
13 | # env:
14 | # TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
15 | # TURBO_TEAM: ${{ vars.TURBO_TEAM }}
16 | strategy:
17 | matrix:
18 | node-version: [18.x, 20.x]
19 | steps:
20 | - name: Checkout Repo
21 | uses: actions/checkout@v4
22 | with:
23 | fetch-depth: 0
24 |
25 | - name: Setup pnpm 8
26 | uses: pnpm/action-setup@v2
27 | with:
28 | version: 8.6.9
29 |
30 | - name: Use Node.js ${{ matrix.node-version }}
31 | uses: actions/setup-node@v2
32 | with:
33 | node-version: ${{ matrix.node-version }}
34 | cache: 'pnpm'
35 |
36 | - name: Install dependencies
37 | run: pnpm install --frozen-lockfile
38 |
39 | - name: Install Playwright Browsers
40 | run: pnpm exec playwright install --with-deps
41 |
42 | - name: Run tests
43 | run: pnpm test
44 |
--------------------------------------------------------------------------------
/.github/workflows/quality.yml:
--------------------------------------------------------------------------------
1 | name: Quality
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | jobs:
10 | prettier:
11 | name: 'Prettier'
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup pnpm
18 | uses: pnpm/action-setup@v2.2.4
19 | with:
20 | version: 8.6.9
21 |
22 | - name: Use Node.js 18
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: '18'
26 | cache: 'pnpm'
27 |
28 | - name: Install dependencies
29 | run: pnpm install --frozen-lockfile
30 |
31 | - name: Run Prettier check
32 | run: pnpm run prettier-check
33 |
34 | eslint:
35 | name: 'ESLint'
36 | runs-on: ubuntu-latest
37 | steps:
38 | - name: Checkout
39 | uses: actions/checkout@v3
40 |
41 | - name: Setup pnpm
42 | uses: pnpm/action-setup@v2.2.4
43 | with:
44 | version: 8
45 |
46 | - name: Use Node.js 18
47 | uses: actions/setup-node@v3
48 | with:
49 | node-version: '18'
50 | cache: 'pnpm'
51 |
52 | - name: Install dependencies
53 | run: pnpm install --frozen-lockfile
54 |
55 | - name: Run ESLint check
56 | run: pnpm run lint
57 |
58 | types:
59 | name: 'TypeScript'
60 | runs-on: ubuntu-latest
61 | steps:
62 | - name: Checkout Repo
63 | uses: actions/checkout@v4
64 | with:
65 | fetch-depth: 0
66 |
67 | - name: Setup pnpm 8
68 | uses: pnpm/action-setup@v2
69 | with:
70 | version: 8.6
71 |
72 | - name: Use Node.js 18
73 | uses: actions/setup-node@v3
74 | with:
75 | node-version: '18'
76 | cache: 'pnpm'
77 |
78 | - name: Install dependencies
79 | run: pnpm install --frozen-lockfile
80 |
81 | - name: Run TypeScript type check
82 | run: pnpm run type-check
83 |
--------------------------------------------------------------------------------
/.github/workflows/release-snapshot.yml:
--------------------------------------------------------------------------------
1 | # Snapshot Releases Workflow
2 |
3 | # Purpose
4 | # This workflow allows you to create snapshot releases for testing changes in a pull request
5 | # before a full release, without entering pre-release mode.
6 |
7 | # Problem
8 | # Changesets enforce pre-release mode across all packages in our mono repo, blocking stable
9 | # releases until pre-release mode is exited.
10 |
11 | # Snapshot Releases
12 | # This workflow enables one-off releases from specific branches, known as snapshot releases.
13 | # These are published under the `snapshot` dist-tag with versions like
14 | # `0.4.0-b16419cd576a883e1ddde01bd2fe3f5f54bcc52a-20230913164912`, which include the
15 | # generated version, commit hash, and timestamp.
16 |
17 | # Creating a Snapshot Release
18 | # 1. Push your branch to GitHub and commit a changeset. Generate a changeset with `pnpm changeset`.
19 | # 2. Go to the LangbaseInc/langbase-sdk repository on GitHub.
20 | # 3. Navigate to Actions > Release Snapshot.
21 | # 4. Click "Run workflow", select your branch, and click "Run workflow".
22 | # Inspired by turbo repo.
23 |
24 | name: Release Snapshot
25 |
26 | on:
27 | workflow_dispatch:
28 |
29 | jobs:
30 | release-snapshot:
31 | name: Release Snapshot
32 | runs-on: ubuntu-latest
33 | steps:
34 | - name: Checkout Repo
35 | uses: actions/checkout@v4
36 | with:
37 | fetch-depth: 0
38 |
39 | - name: Setup pnpm 8
40 | uses: pnpm/action-setup@v2
41 | with:
42 | version: 8.6.9
43 |
44 | - name: Setup Node.js 18.x
45 | uses: actions/setup-node@v2
46 | with:
47 | node-version: 18.x
48 |
49 | - name: Add npm auth token to pnpm
50 | run: pnpm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
51 | env:
52 | NPM_TOKEN: ${{secrets.NPM_TOKEN}}
53 |
54 | - name: Install Dependencies
55 | run: pnpm i
56 |
57 | - name: Build
58 | run: pnpm clean && pnpm build
59 |
60 | - name: Add SHORT_SHA env property with commit short sha
61 | run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV
62 |
63 | - name: Create Snapshot Release
64 | run: |
65 | pnpm changeset version --snapshot ${SHORT_SHA}
66 | pnpm clean-examples
67 | pnpm changeset publish --no-git-tag --tag snapshot
68 | env:
69 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
71 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - '.changeset/**'
9 | - '.github/workflows/release.yml'
10 | workflow_dispatch:
11 |
12 | concurrency: ${{ github.workflow }}-${{ github.ref }}
13 |
14 | jobs:
15 | release:
16 | name: Release
17 | runs-on: ubuntu-latest
18 | timeout-minutes: 10
19 | steps:
20 | - name: Checkout Repo
21 | uses: actions/checkout@v4
22 | with:
23 | fetch-depth: 0
24 |
25 | - name: Setup pnpm 8
26 | uses: pnpm/action-setup@v2
27 | with:
28 | version: 8.6.9
29 |
30 | - name: Setup Node.js 18.x
31 | uses: actions/setup-node@v2
32 | with:
33 | node-version: 18.x
34 |
35 | - name: Install Dependencies
36 | run: pnpm i
37 |
38 | - name: Create Release Pull Request or Publish to npm
39 | id: changesets
40 | uses: changesets/action@v1
41 | with:
42 | # This expects you to have a script called release which does a build for your packages and calls changeset publish
43 | publish: pnpm release
44 | env:
45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
47 |
48 | # - name: Send a Slack notification if a publish happens
49 | # if: steps.changesets.outputs.published == 'true'
50 | # # You can do something when a publish happens.
51 | # run: my-slack-bot send-notification --message "A new version of ${GITHUB_REPOSITORY} was published!"
52 |
--------------------------------------------------------------------------------
/.github/workflows/rv.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - '.changeset/**'
9 | - '.github/workflows/release.yml'
10 | workflow_dispatch:
11 |
12 | concurrency: ${{ github.workflow }}-${{ github.ref }}
13 |
14 | jobs:
15 | release:
16 | name: Release
17 | runs-on: ubuntu-latest
18 | timeout-minutes: 10
19 | steps:
20 | - name: Checkout Repo
21 | uses: actions/checkout@v3
22 | with:
23 | fetch-depth: 0
24 |
25 | - name: Setup pnpm 8
26 | uses: pnpm/action-setup@v2
27 | with:
28 | version: 8.6.9
29 |
30 | - name: Setup Node.js 18.x
31 | uses: actions/setup-node@v2
32 | with:
33 | node-version: 18.x
34 |
35 | - name: Install Dependencies
36 | run: pnpm i
37 |
38 | - name: Create Release Pull Request or Publish to npm
39 | id: changesets
40 | uses: changesets/action@v1
41 | with:
42 | # This expects you to have a script called release which does a build for your packages and calls changeset publish
43 | version: pnpm ci:version
44 | publish: pnpm ci:release
45 | env:
46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47 | NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}
48 |
49 | # - name: Get package version
50 | # id: package-version
51 | # run: echo "version=$(node -p "require('./packages/core/package.json').version")" >> "$GITHUB_OUTPUT"
52 |
53 | # - name: Create Git tag
54 | # run: git tag v${{ steps.package-version.outputs.version }}
55 |
56 | # - name: Push Git tag
57 | # run: git push origin v${{ steps.package-version.outputs.version }}
58 |
59 | # - name: Create GitHub Release
60 | # uses: actions/create-release@v1
61 | # env:
62 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63 | # with:
64 | # tag_name: v${{ steps.package-version.outputs.version }}
65 | # release_name: Release v${{ steps.package-version.outputs.version }}
66 | # draft: false
67 | # prerelease: false
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | test-results
3 | node_modules
4 | .turbo
5 | *.log
6 | .next
7 | dist
8 | dist-ssr
9 | *.local
10 | .env
11 | .cache
12 | server/dist
13 | public/dist
14 | .turbo
15 | /test-results/
16 | /playwright-report/
17 | /blob-report/
18 | /playwright/.cache/
19 | .x*
20 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LangbaseInc/langbase-sdk/32acf46cf307910adca31f105f6bdd85c9faba51/.husky/pre-commit
--------------------------------------------------------------------------------
/.kodiak.toml:
--------------------------------------------------------------------------------
1 | # .kodiak.toml
2 | version = 1
3 |
4 | [merge]
5 | automerge_label = "automerge"
6 | require_automerge_label = true
7 | method = "squash"
8 | delete_branch_on_merge = true
9 | optimistic_updates = false
10 | prioritize_ready_to_merge = true
11 | notify_on_conflict = false
12 |
13 | [merge.message]
14 | title = "pull_request_title"
15 | body = "pull_request_body"
16 | include_pr_number = true
17 | body_type = "markdown"
18 | strip_html_comments = true
19 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | auto-install-peers = true
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .next
2 | .nuxt
3 | node_modules
4 | dist
5 | .svelte-kit
6 | .solid
7 | _nuxt
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": false,
3 | "trailingComma": "all",
4 | "arrowParens": "avoid",
5 | "singleQuote": true,
6 | "printWidth": 80,
7 | "useTabs": true,
8 | "tabWidth": 4,
9 | "semi": true
10 | }
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [
3 | {
4 | "mode": "auto"
5 | }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/spellright.dict:
--------------------------------------------------------------------------------
1 | langbase
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We welcome contributions to this project.
4 |
5 | ---
6 |
7 | ## Releasing a snapshot version
8 |
9 | To release a snapshot version to test changes, run the following command:
10 |
11 | ```bash
12 | npm run snapshot
13 | ```
14 |
15 | ## Releasing a new version
16 |
17 | ```bash
18 | pnpm changeset
19 | pnpm version-packages
20 | grlz 'new version'
21 | pnpm release
22 | pnpm update-examples
23 | ```
24 |
25 | ## Testing locally
26 |
27 | To test the changes locally, you can run the following command:
28 |
29 | - Navigate to an example's folder like the Next.js one in `examples/nextjs`.
30 |
31 | - Change the `package.json` to point to the local package for `langbase`.
32 |
33 | ```json
34 | {
35 | "dependencies": {
36 | "langbase": "workspace:*"
37 | }
38 | }
39 | ```
40 |
41 | - Now run in the root:
42 |
43 | ```bash
44 | pnpm clean-all && pnpm install
45 | ```
46 |
47 | Then run the development server:
48 |
49 | ```bash
50 | pnpm dev
51 | ```
52 |
53 | By doing this, the Next.js example will use the local packages instead of the published ones.
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2023 Langbase, Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Langbase SDK
2 |
3 | The AI SDK for building declarative and composable AI-powered LLM products.
4 |
5 | ## Documentation
6 |
7 | Check the [Langbase SDK documentation](https://langbase.com/docs/sdk) for more details.
8 |
9 | The following examples are for reference only. Prefer docs for the latest information.
10 |
11 | ## Getting Started with `langbase` SDK
12 |
13 | ### Installation
14 |
15 | First, install the `langbase` package using npm or yarn:
16 |
17 | ```bash
18 | npm install langbase
19 | ```
20 |
21 | or
22 |
23 | ```bash
24 | pnpm add langbase
25 | ```
26 |
27 | or
28 |
29 | ```bash
30 | yarn add langbase
31 | ```
32 |
33 | ### Usage
34 |
35 | You can [`langbase.pipes.run()`](https://langbase.com/docs/sdk/pipe/run) to generate or stream from a Pipe.
36 |
37 | Check our [SDK documentation](https://langbase.com/docs/sdk) for more details.
38 |
39 | ### Example projects
40 |
41 | Check the following examples:
42 |
43 | - [Node: Generate Text](https://github.com/LangbaseInc/langbase-sdk/blob/main/examples/nodejs/pipes/pipe.run.ts)
44 | - [Node: Stream Text](https://github.com/LangbaseInc/langbase-sdk/blob/main/examples/nodejs/pipes/pipe.run.stream.ts)
45 | - [Next.js Example](https://github.com/LangbaseInc/langbase-sdk/tree/main/examples/nextjs)
46 | - TypeScript code
47 | - [React component](https://github.com/LangbaseInc/langbase-sdk/tree/main/examples/nextjs/components/langbase) to display the response
48 | - [API Route handlers](https://github.com/LangbaseInc/langbase-sdk/tree/main/examples/nextjs/app/langbase/pipe/run) to send requests to ⌘ Langbase
49 |
50 | ### Node.js Example Code
51 |
52 | ## Node.js Examples
53 |
54 | ### Add a `.env` file with your LANGBASE API key
55 |
56 | ```bash
57 | # Add your Langbase API key here: https://langbase.com/docs/api-reference/api-keys
58 | LANGBASE_API_KEY="your-api-key"
59 | ```
60 |
61 | ---
62 |
63 | ### Generate text [`langbase.pipes.run()`](https://langbase.com/docs/sdk/pipe/run)
64 |
65 | Set the `stream` to `false`. For more, check the API reference of [`langbase.pipes.run()`](https://langbase.com/docs/langbase-sdk/generate-text)
66 |
67 | ```ts
68 | import 'dotenv/config';
69 | import {Langbase} from 'langbase';
70 |
71 | // 1. Initiate the Langbase.
72 | const langbase = new Langbase({
73 | // Make sure you have a .env file with LANGBASE_API_KEY.
74 | apiKey: process.env.LANGBASE_API_KEY!,
75 | });
76 |
77 | async function main() {
78 | // 2. Run the pipe with a question.
79 | const response = await langbase.pipes.run({
80 | stream: false,
81 | name: 'summary' // pipe name to run
82 | messages: [
83 | {
84 | role: 'user',
85 | content: 'Who is an AI Engineer?',
86 | },
87 | ],
88 | });
89 |
90 | // 3. Print the response.
91 | console.log('response: ', response);
92 | }
93 |
94 | main();
95 | ```
96 |
97 | ---
98 |
99 | ### Stream text [`langbase.pipes.run()`](https://langbase.com/docs/sdk/pipe/run)
100 |
101 | Set the `stream` to `true`. For more, check the API reference of [`langbase.pipes.run()`](https://langbase.com/docs/langbase-sdk/generate-text)
102 |
103 | ```ts
104 | import 'dotenv/config';
105 | import {getRunner, Langbase} from 'langbase';
106 |
107 | // 1. Initiate the Langbase.
108 | const langbase = new Langbase({
109 | // Make sure you have a .env file with LANGBASE_API_KEY.
110 | apiKey: process.env.LANGBASE_API_KEY!,
111 | });
112 |
113 | async function main() {
114 | const userMsg = 'Who is an AI Engineer?';
115 |
116 | // 2. Run the pipe with a question.
117 | const {stream} = await langbase.pipes.run({
118 | stream: true,
119 | name: 'summary', // pipe name to run
120 | messages: [{role: 'user', content: userMsg}],
121 | });
122 |
123 | // 3. Get the runner and listen to the content.
124 | const runner = getRunner(stream);
125 |
126 | // 4. Print the response.
127 | runner.on('content', content => {
128 | process.stdout.write(content);
129 | });
130 | }
131 |
132 | main();
133 | ```
134 |
135 | Check out [more examples in the docs](https://langbase.com/docs/sdk/examples) →
136 |
--------------------------------------------------------------------------------
/examples/nextjs/.env.local.example:
--------------------------------------------------------------------------------
1 | # !! SERVER SIDE ONLY !!
2 | # Pipes.
3 | LANGBASE_PIPE_LESS_WORDY=""
4 |
--------------------------------------------------------------------------------
/examples/nextjs/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/nextjs/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/nextjs/README.md:
--------------------------------------------------------------------------------
1 | # ⌘ Langbase SDK Next.js Example
2 |
3 | - [Next.js Example](https://github.com/LangbaseInc/langbase-sdk/tree/main/examples/nextjs)
4 | - TypeScript code
5 | - [React component](https://github.com/LangbaseInc/langbase-sdk/tree/main/examples/nextjs/components/langbase) to display the response
6 | - [API Route handlers](https://github.com/LangbaseInc/langbase-sdk/tree/main/examples/nextjs/app/api/langbase/pipe) to send requests to ⌘ Langbase
7 |
8 | First, run the development server:
9 |
10 | ```bash
11 | npm run dev
12 | # or
13 | yarn dev
14 | # or
15 | pnpm dev
16 | # or
17 | bun dev
18 | ```
19 |
20 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
21 |
--------------------------------------------------------------------------------
/examples/nextjs/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LangbaseInc/langbase-sdk/32acf46cf307910adca31f105f6bdd85c9faba51/examples/nextjs/app/favicon.ico
--------------------------------------------------------------------------------
/examples/nextjs/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 0 0% 3.9%;
9 | --card: 0 0% 100%;
10 | --card-foreground: 0 0% 3.9%;
11 | --popover: 0 0% 100%;
12 | --popover-foreground: 0 0% 3.9%;
13 | --primary: 0 0% 9%;
14 | --primary-foreground: 0 0% 98%;
15 | --secondary: 0 0% 96.1%;
16 | --secondary-foreground: 0 0% 9%;
17 | --muted: 0 0% 96.1%;
18 | --muted-foreground: 0 0% 45.1%;
19 | --accent: 0 0% 96.1%;
20 | --accent-foreground: 0 0% 9%;
21 | --destructive: 0 84.2% 60.2%;
22 | --destructive-foreground: 0 0% 98%;
23 | --border: 0 0% 89.8%;
24 | --input: 0 0% 89.8%;
25 | --ring: 0 0% 3.9%;
26 | --radius: 0.5rem;
27 | --chart-1: 12 76% 61%;
28 | --chart-2: 173 58% 39%;
29 | --chart-3: 197 37% 24%;
30 | --chart-4: 43 74% 66%;
31 | --chart-5: 27 87% 67%;
32 | }
33 |
34 | .dark {
35 | --background: 0 0% 3.9%;
36 | --foreground: 0 0% 98%;
37 | --card: 0 0% 3.9%;
38 | --card-foreground: 0 0% 98%;
39 | --popover: 0 0% 3.9%;
40 | --popover-foreground: 0 0% 98%;
41 | --primary: 0 0% 98%;
42 | --primary-foreground: 0 0% 9%;
43 | --secondary: 0 0% 14.9%;
44 | --secondary-foreground: 0 0% 98%;
45 | --muted: 0 0% 14.9%;
46 | --muted-foreground: 0 0% 63.9%;
47 | --accent: 0 0% 14.9%;
48 | --accent-foreground: 0 0% 98%;
49 | --destructive: 0 62.8% 30.6%;
50 | --destructive-foreground: 0 0% 98%;
51 | --border: 0 0% 14.9%;
52 | --input: 0 0% 14.9%;
53 | --ring: 0 0% 83.1%;
54 | --chart-1: 220 70% 50%;
55 | --chart-2: 160 60% 45%;
56 | --chart-3: 30 80% 55%;
57 | --chart-4: 280 65% 60%;
58 | --chart-5: 340 75% 55%;
59 | }
60 | }
61 |
62 | @layer base {
63 | * {
64 | @apply border-border;
65 | }
66 | body {
67 | @apply bg-background text-foreground;
68 | }
69 | }
--------------------------------------------------------------------------------
/examples/nextjs/app/langbase/pipe/run-stream/route.ts:
--------------------------------------------------------------------------------
1 | import {Langbase} from 'langbase';
2 | import {NextRequest} from 'next/server';
3 |
4 | export async function POST(req: NextRequest) {
5 | const options = await req.json();
6 |
7 | // 1. Initiate the Pipe.
8 | const langbase = new Langbase({
9 | apiKey: process.env.LANGBASE_API_KEY!,
10 | });
11 |
12 | // 2. Generate a stream by asking a question
13 | const {stream, threadId} = await langbase.pipes.run({
14 | messages: options.messages,
15 | stream: true,
16 | name: 'summary',
17 | });
18 |
19 | // 3. Done, return the stream in a readable stream format.
20 | return new Response(stream, {
21 | status: 200,
22 | headers: {
23 | 'lb-thread-id': threadId ?? '',
24 | },
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/examples/nextjs/app/langbase/pipe/run/route.ts:
--------------------------------------------------------------------------------
1 | import {Langbase} from 'langbase';
2 | import {NextRequest} from 'next/server';
3 |
4 | export async function POST(req: NextRequest) {
5 | const {prompt} = await req.json();
6 |
7 | // 1. Initiate the Pipe.
8 | const langbase = new Langbase({
9 | apiKey: process.env.LANGBASE_API_KEY!,
10 | });
11 |
12 | // 2. Generate a stream by asking a question
13 | const result = await langbase.pipes.run({
14 | messages: [{role: 'user', content: prompt}],
15 | name: 'summary',
16 | stream: false,
17 | });
18 |
19 | // 3. Done, return the stream in a readable stream format.
20 | return new Response(JSON.stringify(result));
21 | }
22 |
--------------------------------------------------------------------------------
/examples/nextjs/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import {cn} from 'mxcn';
2 | import type {Metadata} from 'next';
3 | import {Inter as FontSans} from 'next/font/google';
4 | import './globals.css';
5 |
6 | const fontSans = FontSans({
7 | subsets: ['latin'],
8 | variable: '--font-sans',
9 | });
10 |
11 | export const metadata: Metadata = {
12 | title: '⌘ Langbase with a Next App',
13 | description: 'Generated by create next app',
14 | };
15 |
16 | export default function RootLayout({
17 | children,
18 | }: Readonly<{
19 | children: React.ReactNode;
20 | }>) {
21 | return (
22 |
23 |
29 | {children}
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/examples/nextjs/app/page.tsx:
--------------------------------------------------------------------------------
1 | import RunNonStreamExample from '@/components/langbase/run';
2 | import RunStreamExample from '@/components/langbase/run-stream';
3 |
4 | export default function Home() {
5 | return (
6 |
7 |
8 |
9 |
10 | ⌘ Langbase AI Pipe
11 |
12 |
13 | An AI agent that responds to your prompts.
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/examples/nextjs/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
--------------------------------------------------------------------------------
/examples/nextjs/components/langbase/chat-advanced.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import {Button} from '@/components/ui/button';
3 | import {Input} from '@/components/ui/input';
4 | import {Message} from 'langbase';
5 | import {usePipe} from 'langbase/react';
6 | import React, {useCallback} from 'react';
7 |
8 | const ChatAdvanced: React.FC = () => {
9 | const handleResponse = useCallback((message: Message) => {
10 | console.log(
11 | 'Received response:',
12 | message.content?.slice(0, 50) + '...',
13 | );
14 | }, []);
15 |
16 | const handleFinish = useCallback((messages: Message[]) => {
17 | console.log(
18 | `Conversation finished. Total messages: ${messages.length}`,
19 | );
20 | }, []);
21 |
22 | const handleError = useCallback((error: Error) => {
23 | console.error('An error occurred:', error);
24 | }, []);
25 |
26 | const {
27 | messages,
28 | input,
29 | handleInputChange,
30 | handleSubmit,
31 | isLoading,
32 | error,
33 | regenerate,
34 | stop,
35 | setMessages,
36 | threadId,
37 | sendMessage,
38 | } = usePipe({
39 | stream: true,
40 | apiRoute: '/langbase/pipe/run-stream',
41 | onResponse: handleResponse,
42 | onFinish: handleFinish,
43 | onError: handleError,
44 | // initialMessages: [
45 | // {role: 'assistant', content: 'Hello! How can I help you?'},
46 | // {role: 'user', content: 'Who is an AI engineer?'},
47 | // ], // You can set initial messages here if needed
48 | // You can set a threadId here if needed to change the thread manually,
49 | // otherwise it will be generated automatically persistented in the browser.
50 | // threadId: '',
51 | });
52 |
53 | const handleClearChat = () => {
54 | setMessages([]);
55 | };
56 |
57 | const handleRegenerateWithOptions = () => {
58 | regenerate({
59 | headers: {'Custom-Header': 'Regenerate'},
60 | body: {customOption: 'regenerateValue'},
61 | });
62 | };
63 |
64 | const handleCustomMessage = () => {
65 | sendMessage('This is a custom message', {
66 | data: {context: 'custom context'},
67 | allowEmptySubmit: false,
68 | });
69 | };
70 |
71 | return (
72 |
73 |
74 | Thread ID: {threadId || 'Not available'}
75 |
76 |
77 | {messages.map((m, index) => (
78 |
84 | {m.role === 'user' ? 'You: ' : 'AI: '}
85 | {m.content}
86 |
87 | ))}
88 |
89 |
90 |
91 | {isLoading && (
92 |
93 | AI is thinking...
94 |
95 | )}
96 |
97 | {error && (
98 |
99 | Error: {error.message}
100 |
101 | )}
102 |
103 |
104 |
111 |
114 |
121 |
128 |
129 |
130 |
144 |
145 |
146 | );
147 | };
148 |
149 | export default ChatAdvanced;
150 |
--------------------------------------------------------------------------------
/examples/nextjs/components/langbase/generate-text.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import {Button} from '@/components/ui/button';
4 | import {Input} from '@/components/ui/input';
5 | import {useState} from 'react';
6 |
7 | export default function GenerateTextExample() {
8 | const [prompt, setPrompt] = useState('');
9 | const [completion, setCompletion] = useState('');
10 | const [loading, setLoading] = useState(false);
11 |
12 | const handleSubmit = async (e: any) => {
13 | e.preventDefault();
14 | if (!prompt.trim()) return;
15 |
16 | setLoading(true);
17 | try {
18 | const response = await fetch('/langbase/pipe/generate-text', {
19 | method: 'POST',
20 | headers: {
21 | 'Content-Type': 'application/json',
22 | },
23 | body: JSON.stringify({prompt}),
24 | });
25 |
26 | if (!response.ok) {
27 | throw new Error('Network response was not ok');
28 | }
29 |
30 | const data = await response.json();
31 | setCompletion(data.completion);
32 | } catch (error) {
33 | console.error('Error:', error);
34 | setCompletion('An error occurred while generating the completion.');
35 | } finally {
36 | setLoading(false);
37 | }
38 | };
39 |
40 | return (
41 |
42 |
43 |
44 | 3. Generate Text{' '}
45 |
49 | `generateText()`
50 | {' '}
51 | with Route Handler
52 |
53 |
54 | Ask a prompt to generate a text completion.
55 |
56 |
57 |
73 |
74 | {!loading && completion && (
75 |
76 | Generated completion: {completion}
77 |
78 | )}
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/examples/nextjs/components/langbase/run-stream.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import {Button} from '@/components/ui/button';
4 | import {Input} from '@/components/ui/input';
5 | import {getRunner} from 'langbase';
6 | import {useState} from 'react';
7 |
8 | export default function RunStreamExample() {
9 | const [prompt, setPrompt] = useState('');
10 | const [completion, setCompletion] = useState('');
11 | const [loading, setLoading] = useState(false);
12 |
13 | const handleSubmit = async (e: React.FormEvent) => {
14 | e.preventDefault();
15 | if (!prompt.trim() || loading) return;
16 |
17 | setLoading(true);
18 | setCompletion('');
19 |
20 | try {
21 | const response = await fetch('/langbase/pipe/run-stream', {
22 | method: 'POST',
23 | body: JSON.stringify([{role: 'user', content: prompt}]),
24 | headers: {'Content-Type': 'text/plain'},
25 | });
26 |
27 | if (response.body) {
28 | const stream = getRunner(response.body);
29 |
30 | // Method #1 to get all of the chunk.
31 | for await (const chunk of stream) {
32 | const content = chunk?.choices[0]?.delta?.content;
33 | content && setCompletion(prev => prev + content);
34 | }
35 |
36 | // // Method #2 to get only the chunk's content as delta of the chunks
37 | // stream.on('content', content => {
38 | // setCompletion(prev => prev + content);
39 | // });
40 | }
41 | } catch (error) {
42 | setLoading(false);
43 | console.error('Error:', error);
44 | } finally {
45 | setLoading(false);
46 | }
47 | };
48 |
49 | return (
50 |
51 |
52 |
53 | 1. Stream Text{' '}
54 |
58 | `pipe.run()`
59 | {' '}
60 | with Route Handler
61 |
62 |
63 | Ask a prompt to stream a text completion.
64 |
65 |
66 |
81 | {completion && (
82 |
83 | Stream: {completion}
84 |
85 | )}
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/examples/nextjs/components/langbase/run.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import {Button} from '@/components/ui/button';
4 | import {Input} from '@/components/ui/input';
5 | import {useState} from 'react';
6 |
7 | export default function RunNonStreamExample() {
8 | const [prompt, setPrompt] = useState('');
9 | const [completion, setCompletion] = useState('');
10 | const [loading, setLoading] = useState(false);
11 |
12 | const handleSubmit = async (e: any) => {
13 | e.preventDefault();
14 | if (!prompt.trim()) return;
15 |
16 | setLoading(true);
17 | try {
18 | const response = await fetch('/langbase/pipe/run', {
19 | method: 'POST',
20 | headers: {
21 | 'Content-Type': 'application/json',
22 | },
23 | body: JSON.stringify({prompt}),
24 | });
25 |
26 | if (!response.ok) {
27 | throw new Error('Network response was not ok');
28 | }
29 |
30 | const data = await response.json();
31 | setCompletion(data.completion);
32 | } catch (error) {
33 | console.error('Error:', error);
34 | setCompletion('An error occurred while generating the completion.');
35 | } finally {
36 | setLoading(false);
37 | }
38 | };
39 |
40 | return (
41 |
42 |
43 |
44 | 2. Generate Text{' '}
45 |
49 | `pipe.run()`
50 | {' '}
51 | with Route Handler
52 |
53 |
54 | Ask a prompt to generate a text completion.
55 |
56 |
57 |
73 |
74 | {!loading && completion && (
75 |
76 | Generated completion: {completion}
77 |
78 | )}
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/examples/nextjs/components/langbase/stream-text.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import {Button} from '@/components/ui/button';
4 | import {Input} from '@/components/ui/input';
5 | import {fromReadableStream} from 'langbase';
6 | import {useState} from 'react';
7 |
8 | export default function StreamTextExample() {
9 | const [prompt, setPrompt] = useState('');
10 | const [completion, setCompletion] = useState('');
11 | const [loading, setLoading] = useState(false);
12 |
13 | const handleSubmit = async (e: React.FormEvent) => {
14 | e.preventDefault();
15 | if (!prompt.trim() || loading) return;
16 |
17 | setLoading(true);
18 | setCompletion('');
19 |
20 | try {
21 | const response = await fetch('/langbase/pipe/stream-text', {
22 | method: 'POST',
23 | body: JSON.stringify({prompt}),
24 | headers: {'Content-Type': 'text/plain'},
25 | });
26 |
27 | if (response.body) {
28 | const stream = fromReadableStream(response.body);
29 |
30 | // Method #1 to get all of the chunk.
31 | for await (const chunk of stream) {
32 | const content = chunk?.choices[0]?.delta?.content;
33 | content && setCompletion(prev => prev + content);
34 | }
35 |
36 | // // Method #2 to get only the chunk's content as delta of the chunks
37 | // stream.on('content', content => {
38 | // setCompletion(prev => prev + content);
39 | // });
40 | }
41 | } catch (error) {
42 | setLoading(false);
43 | console.error('Error:', error);
44 | } finally {
45 | setLoading(false);
46 | }
47 | };
48 |
49 | return (
50 |
51 |
52 |
53 | 4. Stream Text{' '}
54 |
58 | `streamText()`
59 | {' '}
60 | with Route Handler
61 |
62 |
63 | Ask a prompt to stream a text completion.
64 |
65 |
66 |
81 | {completion && (
82 |
83 | Stream: {completion}
84 |
85 | )}
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/examples/nextjs/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Slot} from '@radix-ui/react-slot';
3 | import {cva, type VariantProps} from 'class-variance-authority';
4 |
5 | import {cn} from '@/lib/utils';
6 |
7 | const buttonVariants = cva(
8 | 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | 'bg-primary text-primary-foreground hover:bg-primary/90',
14 | destructive:
15 | 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
16 | outline:
17 | 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
18 | secondary:
19 | 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
20 | ghost: 'hover:bg-accent hover:text-accent-foreground',
21 | link: 'text-primary underline-offset-4 hover:underline',
22 | },
23 | size: {
24 | default: 'h-10 px-4 py-2',
25 | sm: 'h-9 rounded-md px-3',
26 | lg: 'h-11 rounded-md px-8',
27 | icon: 'h-10 w-10',
28 | },
29 | },
30 | defaultVariants: {
31 | variant: 'default',
32 | size: 'default',
33 | },
34 | },
35 | );
36 |
37 | export interface ButtonProps
38 | extends React.ButtonHTMLAttributes,
39 | VariantProps {
40 | asChild?: boolean;
41 | }
42 |
43 | const Button = React.forwardRef(
44 | ({className, variant, size, asChild = false, ...props}, ref) => {
45 | const Comp = asChild ? Slot : 'button';
46 | return (
47 |
52 | );
53 | },
54 | );
55 | Button.displayName = 'Button';
56 |
57 | export {Button, buttonVariants};
58 |
--------------------------------------------------------------------------------
/examples/nextjs/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import {cn} from '@/lib/utils';
4 |
5 | export interface InputProps
6 | extends React.InputHTMLAttributes {}
7 |
8 | const Input = React.forwardRef(
9 | ({className, type, ...props}, ref) => {
10 | return (
11 |
20 | );
21 | },
22 | );
23 | Input.displayName = 'Input';
24 |
25 | export {Input};
26 |
--------------------------------------------------------------------------------
/examples/nextjs/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import {type ClassValue, clsx} from 'clsx';
2 | import {twMerge} from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/examples/nextjs/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/nextjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@radix-ui/react-slot": "^1.1.0",
13 | "class-variance-authority": "^0.7.0",
14 | "clsx": "^2.1.1",
15 | "langbase": "^1.1.59",
16 | "lucide-react": "^0.416.0",
17 | "mxcn": "^2.0.0",
18 | "next": "14.2.5",
19 | "openai": "^4.53.0",
20 | "react": "^18",
21 | "react-dom": "^18",
22 | "tailwind-merge": "^2.4.0",
23 | "tailwindcss-animate": "^1.0.7"
24 | },
25 | "devDependencies": {
26 | "@types/node": "^20",
27 | "@types/react": "^18",
28 | "@types/react-dom": "^18",
29 | "eslint": "^8",
30 | "eslint-config-next": "14.2.5",
31 | "mini-css-extract-plugin": "^2.9.0",
32 | "postcss": "^8",
33 | "tailwindcss": "^3.4.1",
34 | "typescript": "^5"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/nextjs/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/nextjs/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/nextjs/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/nextjs/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | const {fontFamily} = require('tailwindcss/defaultTheme');
2 | import type {Config} from 'tailwindcss';
3 |
4 | const config = {
5 | darkMode: ['class'],
6 | content: [
7 | './pages/**/*.{ts,tsx}',
8 | './components/**/*.{ts,tsx}',
9 | './app/**/*.{ts,tsx}',
10 | './src/**/*.{ts,tsx}',
11 | ],
12 | prefix: '',
13 | theme: {
14 | container: {
15 | center: true,
16 | padding: '2rem',
17 | screens: {
18 | '2xl': '1400px',
19 | },
20 | },
21 | extend: {
22 | fontFamily: {
23 | sans: ['var(--font-sans)', ...fontFamily.sans],
24 | },
25 | colors: {
26 | border: 'hsl(var(--border))',
27 | input: 'hsl(var(--input))',
28 | ring: 'hsl(var(--ring))',
29 | background: 'hsl(var(--background))',
30 | foreground: 'hsl(var(--foreground))',
31 | primary: {
32 | DEFAULT: 'hsl(var(--primary))',
33 | foreground: 'hsl(var(--primary-foreground))',
34 | },
35 | secondary: {
36 | DEFAULT: 'hsl(var(--secondary))',
37 | foreground: 'hsl(var(--secondary-foreground))',
38 | },
39 | destructive: {
40 | DEFAULT: 'hsl(var(--destructive))',
41 | foreground: 'hsl(var(--destructive-foreground))',
42 | },
43 | muted: {
44 | DEFAULT: 'hsl(var(--muted))',
45 | foreground: 'hsl(var(--muted-foreground))',
46 | },
47 | accent: {
48 | DEFAULT: 'hsl(var(--accent))',
49 | foreground: 'hsl(var(--accent-foreground))',
50 | },
51 | popover: {
52 | DEFAULT: 'hsl(var(--popover))',
53 | foreground: 'hsl(var(--popover-foreground))',
54 | },
55 | card: {
56 | DEFAULT: 'hsl(var(--card))',
57 | foreground: 'hsl(var(--card-foreground))',
58 | },
59 | },
60 | borderRadius: {
61 | lg: 'var(--radius)',
62 | md: 'calc(var(--radius) - 2px)',
63 | sm: 'calc(var(--radius) - 4px)',
64 | },
65 | keyframes: {
66 | 'accordion-down': {
67 | from: {height: '0'},
68 | to: {height: 'var(--radix-accordion-content-height)'},
69 | },
70 | 'accordion-up': {
71 | from: {height: 'var(--radix-accordion-content-height)'},
72 | to: {height: '0'},
73 | },
74 | },
75 | animation: {
76 | 'accordion-down': 'accordion-down 0.2s ease-out',
77 | 'accordion-up': 'accordion-up 0.2s ease-out',
78 | },
79 | },
80 | },
81 | plugins: [require('tailwindcss-animate')],
82 | } satisfies Config;
83 |
84 | export default config;
85 |
--------------------------------------------------------------------------------
/examples/nextjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/nodejs/.env.example:
--------------------------------------------------------------------------------
1 | # These env vars should always be used on the server side and can be called anything.
2 | PIPE_LESS_WORDY=""
3 | LANGBASE_SDK_GENERATE_PIPE=""
4 | LANGBASE_SDK_CHAT_PIPE=""
5 | LANGBASE_API_KEY=""
6 | # https://dashboard.exa.ai/api-keys
7 | EXA_API_KEY=""
8 | CRAWL_KEY=""
9 |
--------------------------------------------------------------------------------
/examples/nodejs/.gitignore:
--------------------------------------------------------------------------------
1 | # NPM #
2 | ##########
3 | # Ignore all directories called node_modules in current folder and any subfolders.
4 | node_modules/
5 | /node_modules/
6 |
7 | # Packages #
8 | ############
9 | *.7z
10 | *.dmg
11 | *.gz
12 | *.bz2
13 | *.iso
14 | *.jar
15 | *.rar
16 | *.tar
17 | *.zip
18 | *.tgz
19 | *.map
20 |
21 | # Logs and databases #
22 | ######################
23 | *.log
24 | *.sql
25 | *.env
26 |
27 | # OS generated files #
28 | ######################
29 | **.DS_Store*
30 | ehthumbs.db
31 | Icon?
32 | Thumbs.db
33 | ._*
34 | **settings.dat*
35 |
36 | # Vim generated files #
37 | ######################
38 | *.un~
39 |
40 | # SASS #
41 | ##########
42 | **/.sass-cache
43 | **/.sass-cache/*
44 | **/.map
45 |
46 | # Composer #
47 | ##########
48 | !assets/js/vendor/
49 | wpcs/
50 | /vendor/
51 |
52 | # Bower #
53 | ##########
54 | assets/bower_components/*
55 |
56 | # Codekit #
57 | ##########
58 | /codekit-config.json
59 | *.codekit
60 | **.codekit-cache/*
61 |
62 | # Compiled Files and Build Dirs #
63 | ##########
64 | /README.html
65 |
66 | # PhpStrom Project Files #
67 | .idea/
68 | library/vendors/composer
69 | assets/img/.DS_Store
70 |
71 | # VSCode related files #
72 | # .vscode
73 |
74 | # Next.js
75 | .next
76 |
--------------------------------------------------------------------------------
/examples/nodejs/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": false,
3 | "trailingComma": "all",
4 | "arrowParens": "avoid",
5 | "singleQuote": true,
6 | "printWidth": 80,
7 | "useTabs": true,
8 | "tabWidth": 4,
9 | "semi": true
10 | }
11 |
--------------------------------------------------------------------------------
/examples/nodejs/chunker/index.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const results = await langbase.chunker({
12 | content: `Langbase is the most powerful serverless AI platform for building AI agents with memory.
13 | Build, deploy, and scale AI agents with tools and memory (RAG). Simple AI primitives with a world-class developer experience without using any frameworks.
14 |
15 | Compared to complex AI frameworks, Langbase is serverless and the first composable AI platform.
16 |
17 | Build AI agents without any bloated frameworks. You write the logic, we handle the logistics.
18 |
19 | Start by building simple AI agents (pipes)
20 | Then train serverless semantic Memory agents (RAG) to get accurate and trusted results
21 | Langbase provides several options to get started:
22 |
23 | AI Studio: Build, collaborate, and deploy AI Agents with tools and Memory (RAG).
24 | Langbase SDK: Easiest wasy to build AI Agents with TypeScript. (recommended)
25 | HTTP API: Build AI agents with any language (Python, Go, PHP, etc.).
26 | or BaseAI.dev: Local-first, open-source web AI framework.`,
27 | chunkOverlap: 256,
28 | chunkMaxLength: 1024,
29 | });
30 |
31 | console.log(results);
32 | }
33 |
34 | main();
35 |
--------------------------------------------------------------------------------
/examples/nodejs/embed/index.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | /**
11 | * Generates embeddings for the given text chunks.
12 | */
13 | async function main() {
14 | const response = await langbase.embed({
15 | chunks: [
16 | 'Langbase is the most powerful serverless platform for building AI agents with memory. Build, scale, and evaluate AI agents with semantic memory (RAG) and world-class developer experience. We process billions of AI messages/tokens daily. Built for every developer, not just AI/ML experts.',
17 | ],
18 | embeddingModel: 'openai:text-embedding-3-large',
19 | });
20 |
21 | console.log(response);
22 | }
23 |
24 | main();
25 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.create.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.memories.create({
10 | name: 'memory-sdk',
11 | embedding_model: 'cohere:embed-multilingual-v3.0',
12 | });
13 |
14 | console.log(response);
15 | }
16 |
17 | main();
18 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.delete.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.memories.delete({
10 | name: 'memory-sdk',
11 | });
12 |
13 | console.log(response);
14 | }
15 |
16 | main();
17 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.docs.delete.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.memories.documents.delete({
10 | memoryName: 'memory-sdk',
11 | documentName: 'readme.md',
12 | });
13 |
14 | console.log(response);
15 | }
16 |
17 | main();
18 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.docs.list.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.memories.documents.list({
10 | memoryName: 'memory-sdk',
11 | });
12 |
13 | console.log(response);
14 | }
15 |
16 | main();
17 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.docs.retry-embed.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.memories.documents.embeddings.retry({
10 | memoryName: 'memory-sdk',
11 | documentName: 'memory.upload.doc.ts',
12 | });
13 |
14 | console.log(response);
15 | }
16 |
17 | main();
18 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.docs.upload.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 | import fs from 'fs';
4 | import path from 'path';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const src = path.join(
12 | process.cwd(),
13 | 'examples',
14 | 'memory',
15 | 'memory.docs.upload.ts',
16 | );
17 |
18 | const response = await langbase.memories.documents.upload({
19 | document: fs.readFileSync(src),
20 | memoryName: 'memory-sdk',
21 | documentName: 'memory.docs.upload.ts',
22 | contentType: 'text/plain',
23 | meta: {
24 | extension: 'ts',
25 | description: 'This is a test file',
26 | },
27 | });
28 |
29 | console.log(response);
30 | }
31 |
32 | main();
33 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.list.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.memories.list();
10 | console.log(response);
11 | }
12 |
13 | main();
14 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.retrieve.filters.And.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Basic example to demonstrate how to retrieve memories with filters.
3 | *
4 | * - And: This filter is used to retrieve memories that match all the filters.
5 | * - Eq: This filter is used to retrieve memories that match the exact value.
6 | *
7 | * In this example, we retrieve memories with the following filters:
8 | * - company: Langbase
9 | * - category: docs
10 | *
11 | * We expect to get all chunks of memory from the Langbase Docs memory that have the company Langbase and the category docs.
12 | *
13 | */
14 |
15 | import 'dotenv/config';
16 | import {Langbase} from 'langbase';
17 |
18 | const langbase = new Langbase({
19 | apiKey: process.env.LANGBASE_API_KEY!,
20 | });
21 |
22 | async function main() {
23 | const response = await langbase.memories.retrieve({
24 | memory: [
25 | {
26 | name: 'langbase-docs',
27 | filters: [
28 | 'And',
29 | [
30 | ['company', 'Eq', 'Langbase'],
31 | ['category', 'Eq', 'docs'],
32 | ],
33 | ],
34 | },
35 | ],
36 | query: 'What are pipes in Langbase Docs?',
37 | topK: 5,
38 | });
39 |
40 | console.log(response);
41 | }
42 |
43 | main();
44 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.retrieve.filters.Eq.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Basic example to demonstrate how to retrieve memories with filters.
3 | *
4 | * - Eq: This filter is used to retrieve memories that match the exact value.
5 | *
6 | * In this example, we retrieve memories with the following filters:
7 | * - company: Langbase
8 | *
9 | * We expect to get all chunks of memory from the Langbase Docs memory that have the company Langbase.
10 | *
11 | */
12 |
13 | import 'dotenv/config';
14 | import {Langbase} from 'langbase';
15 |
16 | const langbase = new Langbase({
17 | apiKey: process.env.LANGBASE_API_KEY!,
18 | });
19 |
20 | async function main() {
21 | const response = await langbase.memories.retrieve({
22 | memory: [
23 | {
24 | name: 'langbase-docs',
25 | filters: ['company', 'Eq', 'Langbase'],
26 | },
27 | ],
28 | query: 'What is Langbase?',
29 | topK: 5,
30 | });
31 |
32 | console.log(response);
33 | }
34 |
35 | main();
36 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.retrieve.filters.In.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Basic example to demonstrate how to retrieve memories with filters.
3 | *
4 | * - In: This filter is used to retrieve memories that match any of the value/values in the array.
5 | *
6 | * In this example, we retrieve memories with the following filters:
7 | * - company: Langbase or Google
8 | *
9 | * We expect to get all chunks of memory from the Langbase Docs memory that have the company Langbase or Google.
10 | *
11 | */
12 |
13 | import 'dotenv/config';
14 | import {Langbase} from 'langbase';
15 |
16 | const langbase = new Langbase({
17 | apiKey: process.env.LANGBASE_API_KEY!,
18 | });
19 |
20 | async function main() {
21 | const response = await langbase.memories.retrieve({
22 | memory: [
23 | {
24 | name: 'langbase-docs',
25 | filters: ['company', 'In', ['Langbase', 'Google']],
26 | },
27 | ],
28 | query: 'What are pipes in Langbase?',
29 | topK: 5,
30 | });
31 |
32 | console.log(response);
33 | }
34 |
35 | main();
36 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.retrieve.filters.NotEq.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Basic example to demonstrate how to retrieve memories with filters.
3 | *
4 | * - NotEq: This filter is used to retrieve memories that do not match the exact value.
5 | *
6 | * In this example, we retrieve memories with the following filters:
7 | * - company: Langbase
8 | *
9 | * We expect to get all chunks of memory from the Langbase Docs memory that do not have the company Langbase.
10 | *
11 | */
12 |
13 | import 'dotenv/config';
14 | import {Langbase} from 'langbase';
15 |
16 | const langbase = new Langbase({
17 | apiKey: process.env.LANGBASE_API_KEY!,
18 | });
19 |
20 | async function main() {
21 | const response = await langbase.memories.retrieve({
22 | memory: [
23 | {
24 | name: 'langbase-docs',
25 | filters: ['company', 'NotEq', 'Google'],
26 | },
27 | ],
28 | query: 'What is Langbase?',
29 | topK: 5,
30 | });
31 |
32 | console.log(response);
33 | }
34 |
35 | main();
36 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.retrieve.filters.NotIn.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Basic example to demonstrate how to retrieve memories with filters.
3 | *
4 | * - NotIn: This filter is used to retrieve memories that do not match any of the value/values in the array.
5 | *
6 | * In this example, we retrieve memories with the following filters:
7 | * - company: Google
8 | *
9 | * We expect to get all chunks of memory from the Langbase Docs memory that do not have the company Google.
10 | *
11 | */
12 |
13 | import 'dotenv/config';
14 | import {Langbase} from 'langbase';
15 |
16 | const langbase = new Langbase({
17 | apiKey: process.env.LANGBASE_API_KEY!,
18 | });
19 |
20 | async function main() {
21 | const response = await langbase.memories.retrieve({
22 | memory: [
23 | {
24 | name: 'langbase-docs',
25 | filters: ['company', 'NotIn', 'Google'],
26 | },
27 | ],
28 | query: 'What are pipes in Langbase?',
29 | topK: 5,
30 | });
31 |
32 | console.log(response);
33 | }
34 |
35 | main();
36 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.retrieve.filters.advanced.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Advanced example to demonstrate how to retrieve memories with filters.
3 | *
4 | * - And: This filter is used to retrieve memories that match all the filters.
5 | * - Or: This filter is used to retrieve memories that match any of the filters.
6 | * - In: This filter is used to retrieve memories that match any of the value/values in the array.
7 | * - Eq: This filter is used to retrieve memories that match the exact value.
8 | *
9 | * In this example, we retrieve memories with the following filters:
10 | * - company: Langbase
11 | * - category: docs or examples
12 | * - primitive: Chunk or Threads
13 | *
14 | * We expect to get all chunks of memory from the Langbase Docs memory
15 | * that have the company Langbase, the category docs or examples, and the primitive can be Chunk or Threads.
16 | *
17 | */
18 |
19 | import 'dotenv/config';
20 | import {Langbase} from 'langbase';
21 |
22 | const langbase = new Langbase({
23 | apiKey: process.env.LANGBASE_API_KEY!,
24 | });
25 |
26 | async function main() {
27 | const response = await langbase.memories.retrieve({
28 | memory: [
29 | {
30 | name: 'langbase-docs',
31 | filters: [
32 | 'And',
33 | [
34 | ['company', 'Eq', 'Langbase'],
35 | [
36 | 'Or',
37 | [
38 | ['category', 'Eq', 'docs'],
39 | ['category', 'Eq', 'examples'],
40 | ],
41 | ],
42 | ['primitive', 'In', ['Chunk', 'Threads']],
43 | ],
44 | ],
45 | },
46 | ],
47 | query: 'What are primitives in Langbase?',
48 | topK: 5,
49 | });
50 |
51 | console.log(response);
52 | }
53 |
54 | main();
55 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/memory.retrieve.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.memories.retrieve({
10 | memory: [
11 | {
12 | name: 'langbase-docs',
13 | },
14 | ],
15 | query: 'What are pipes in Langbase?',
16 | topK: 2,
17 | });
18 |
19 | console.log(response);
20 | }
21 |
22 | main();
23 |
--------------------------------------------------------------------------------
/examples/nodejs/memory/multi-agent-memory-routing.ts:
--------------------------------------------------------------------------------
1 | import dotenv from 'dotenv';
2 | import {Langbase} from 'langbase';
3 |
4 | dotenv.config();
5 |
6 | // Initialize Langbase with your API key
7 | const langbase = new Langbase({
8 | apiKey: process.env.LANGBASE_API_KEY!,
9 | });
10 |
11 | // Router agent checks whether or not to use memory agent.
12 | async function runRouterAgent(query) {
13 | const response = await langbase.pipes.run({
14 | stream: false,
15 | name: 'router-agent',
16 | model: 'openai:gpt-4o-mini', // Ensure this model supports JSON mode
17 | messages: [
18 | {
19 | role: 'system', // Update the content with your memory description
20 | content: `You are an expert query analyzer. Given a query, analyze whether it needs to use the memory agent or not.
21 | The memory agent contains a knowledge base that provides context-aware responses about AI, machine learning,
22 | and related topics. If the query is related to these topics, indicate that the memory agent should be used.
23 | Otherwise, indicate that it should not be used.
24 | Always respond in JSON format with the following structure: {"useMemory": true/false}.`,
25 | },
26 | {role: 'user', content: query},
27 | ],
28 | });
29 |
30 | // Parse the response to determine if we should use the memory agent
31 | const parsedResponse = JSON.parse(response.completion);
32 | return parsedResponse.useMemory;
33 | }
34 |
35 | // Example usage
36 | async function main() {
37 | const query = 'What is AI?';
38 | const useMemory = await runRouterAgent(query);
39 | console.log('Use Memory:', useMemory);
40 |
41 | if (useMemory) {
42 | // Run the memory agent
43 | const response = await langbase.pipes.run({
44 | stream: false,
45 | name: 'memory-agent', // Name of your memory agent
46 | messages: [{role: 'user', content: query}],
47 | });
48 | console.log('Response from memory agent:', response);
49 | } else {
50 | // Run the non-memory agent
51 | const response = await langbase.pipes.run({
52 | stream: false,
53 | name: 'non-memory-agent', // Name of your non-memory agent
54 | messages: [{role: 'user', content: query}],
55 | });
56 | console.log('Response from non-memory agent:', response);
57 | }
58 | }
59 |
60 | main();
61 |
--------------------------------------------------------------------------------
/examples/nodejs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "everything",
4 | "version": "0.0.0",
5 | "description": "Everything example",
6 | "type": "module",
7 | "main": "index.js",
8 | "scripts": {
9 | "pipe.create": "npx tsx ./pipes/pipe.create.ts",
10 | "pipe.run.chat": "npx tsx ./pipes/pipe.run.chat.ts",
11 | "pipe.run.stream.chat": "npx tsx ./pipes/pipe.run.stream.chat.ts",
12 | "memory.create": "npx tsx ./memory/memory.create.ts",
13 | "memory.list": "npx tsx ./memory/memory.list.ts",
14 | "memory.delete": "npx tsx ./memory/memory.delete.ts",
15 | "memory.retrieve": "npx tsx ./memory/memory.retrieve.ts",
16 | "memory.retrieve.filters.In": "npx tsx ./memory/memory.retrieve.filters.In.ts",
17 | "memory.retrieve.filters.NotIn": "npx tsx ./memory/memory.retrieve.filters.NotIn.ts",
18 | "memory.retrieve.filters.Eq": "npx tsx ./memory/memory.retrieve.filters.Eq.ts",
19 | "memory.retrieve.filters.NotEq": "npx tsx ./memory/memory.retrieve.filters.NotEq.ts",
20 | "memory.retrieve.filters.Or": "npx tsx ./memory/memory.retrieve.filters.Or.ts",
21 | "memory.retrieve.filters.advanced": "npx tsx ./memory/memory.retrieve.filters.advanced.ts",
22 | "memory.docs.list": "npx tsx ./memory/memory.docs.list.ts",
23 | "memory.docs.delete": "npx tsx ./memory/memory.docs.delete.ts",
24 | "memory.docs.upload": "npx tsx ./memory/memory.docs.upload.ts",
25 | "memory.docs.retry-embed": "npx tsx ./memory/memory.docs.retry-embed.ts",
26 | "pipe.update": "npx tsx ./pipes/pipe.update.ts",
27 | "pipe.list": "npx tsx ./pipes/pipe.list.ts",
28 | "pipe.run": "npx tsx ./pipes/pipe.run.ts",
29 | "pipe.tool": "npx tsx ./pipes/pipe.tool.ts",
30 | "pipe.tool.stream": "npx tsx ./pipes/pipe.tool.stream.ts",
31 | "pipe.run.stream": "npx tsx ./pipes/pipe.run.stream.ts",
32 | "pipe.run.stream.llmkey": "npx tsx ./pipes/pipe.run.stream.llmkey.ts",
33 | "pipe.structured.outputs": "npx tsx ./pipes/pipe.structured.outputs.ts",
34 | "tools.web-search": "npx tsx ./tools/web-search.ts",
35 | "tools.crawl": "npx tsx ./tools/crawl.ts",
36 | "embed": "npx tsx ./embed/index.ts",
37 | "chunker": "npx tsx ./chunker/index.ts",
38 | "parser": "npx tsx ./parser/index.ts",
39 | "threads.create": "npx tsx ./threads/threads.create.ts",
40 | "threads.update": "npx tsx ./threads/threads.update.ts",
41 | "threads.delete": "npx tsx ./threads/threads.delete.ts",
42 | "threads.append": "npx tsx ./threads/threads.append.ts",
43 | "threads.messages.list": "npx tsx ./threads/threads.messages.list.ts",
44 | "threads.get": "npx tsx ./threads/threads.get.ts",
45 | "workflow": "npx tsx ./workflows/workflows.ts"
46 | },
47 | "keywords": [],
48 | "author": "Ahmad Awais (https://twitter.com/MrAhmadAwais)",
49 | "license": "UNLICENSED",
50 | "dependencies": {
51 | "@langbase/cli": "workspace:*",
52 | "dotenv": "^16.4.5",
53 | "langbase": "^1.1.59",
54 | "uuid": "^11.1.0",
55 | "zod": "^3.21.4",
56 | "zod-to-json-schema": "^3.24.5"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/examples/nodejs/parser/index.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import fs from 'fs';
5 | import {Langbase} from 'langbase';
6 | import path from 'path';
7 |
8 | const langbase = new Langbase({
9 | apiKey: process.env.LANGBASE_API_KEY!,
10 | });
11 |
12 | async function main() {
13 | const documentPath = path.join(process.cwd(), 'parser', 'composable-ai.md');
14 |
15 | const results = await langbase.parser({
16 | document: fs.readFileSync(documentPath),
17 | documentName: 'composable-ai.md',
18 | contentType: 'application/pdf',
19 | });
20 |
21 | console.log(results);
22 | }
23 |
24 | main();
25 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/multi-agent.ts:
--------------------------------------------------------------------------------
1 | // Basic example to demonstrate how to feed the output of an agent as an input to another agent.
2 |
3 | import dotenv from 'dotenv';
4 | import {Langbase} from 'langbase';
5 |
6 | dotenv.config();
7 |
8 | const langbase = new Langbase({
9 | apiKey: process.env.LANGBASE_API_KEY!,
10 | });
11 |
12 | async function main() {
13 | // First agent: Summarize text
14 | const summarizeAgent = async (text: string) => {
15 | const response = await langbase.pipes.run({
16 | stream: false,
17 | name: 'summarize',
18 | messages: [
19 | {
20 | role: 'system',
21 | content: `You are a helpful assistant that summarizes text.`,
22 | },
23 | {
24 | role: 'user',
25 | content: `Summarize the following text: ${text}`,
26 | },
27 | ],
28 | });
29 | return response.completion;
30 | };
31 |
32 | // Second agent: Generate questions
33 | const questionsAgent = async (summary: string) => {
34 | const response = await langbase.pipes.run({
35 | stream: false,
36 | name: 'generate-questions',
37 | messages: [
38 | {
39 | role: 'system',
40 | content: `You are a helpful assistant that generates questions.`,
41 | },
42 | {
43 | role: 'user',
44 | content: `Generate 3 questions based on this summary: ${summary}`,
45 | },
46 | ],
47 | });
48 | return response.completion;
49 | };
50 |
51 | // Router agent: Orchestrate the flow
52 | const workflow = async (inputText: string) => {
53 | const summary = await summarizeAgent(inputText);
54 | const questions = await questionsAgent(summary);
55 | return {summary, questions};
56 | };
57 |
58 | // Example usage
59 | const inputText =
60 | 'Artificial intelligence (AI) is intelligence demonstrated by machines, as opposed to natural intelligence displayed by humans. AI research has been defined as the field of study of intelligent agents, which refers to any system that perceives its environment and takes actions that maximize its chance of achieving its goals.';
61 |
62 | const result = await workflow(inputText);
63 | console.log('Summary:', result.summary);
64 | console.log('Questions:', result.questions);
65 | }
66 |
67 | main().catch(console.error);
68 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.create.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.pipes.create({
10 | name: 'summary',
11 | status: 'private',
12 | });
13 |
14 | console.log(response);
15 | }
16 |
17 | main();
18 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.list.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.pipes.list();
10 | console.log(response);
11 | }
12 |
13 | main();
14 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.run.chat.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | // Message 1: Tell something to the LLM.
10 | const response1 = await langbase.pipes.run({
11 | stream: false,
12 | name: 'summary',
13 | messages: [{role: 'user', content: 'My company is called Langbase'}],
14 | });
15 |
16 | console.log(response1.completion);
17 |
18 | // Message 2: Ask something about the first message.
19 | // Continue the conversation in the same thread by sending
20 | // `threadId` from the second message onwards.
21 | const response2 = await langbase.pipes.run({
22 | name: 'summary',
23 | stream: false,
24 | threadId: response1.threadId!,
25 | messages: [{role: 'user', content: 'Tell me the name of my company?'}],
26 | });
27 |
28 | console.log(response2.completion);
29 | // You'll see any LLM will know the company is `Langbase`
30 | // since it's the same chat thread. This is how you can
31 | // continue a conversation in the same thread.
32 | }
33 |
34 | main();
35 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.run.pipe.key.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {getRunner, Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const userMsg = 'Who is an AI Engineer?';
10 |
11 | // Get readable stream
12 | const {stream, threadId, rawResponse} = await langbase.pipes.run({
13 | messages: [{role: 'user', content: userMsg}],
14 | stream: true,
15 | rawResponse: true,
16 | apiKey: process.env.PIPE_API_KEY!,
17 | });
18 |
19 | // Convert the stream to a stream runner.
20 | const runner = getRunner(stream);
21 |
22 | // Method 1: Using event listeners
23 | runner.on('connect', () => {
24 | console.log('Stream started.\n');
25 | });
26 |
27 | runner.on('content', content => {
28 | process.stdout.write(content);
29 | });
30 |
31 | runner.on('end', () => {
32 | console.log('\nStream ended.');
33 | });
34 |
35 | runner.on('error', error => {
36 | console.error('Error:', error);
37 | });
38 | }
39 |
40 | main();
41 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.run.stream.chat.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase, getRunner} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | // Message 1: Tell something to the LLM.
9 | const response1 = await langbase.pipes.run({
10 | name: 'summary',
11 | stream: true,
12 | messages: [{role: 'user', content: 'My company is called Langbase'}],
13 | });
14 |
15 | const runner1 = getRunner(response1.stream);
16 |
17 | runner1.on('content', content => {
18 | process.stdout.write(content);
19 | });
20 |
21 | // Message 2: Ask something about the first message.
22 | // Continue the conversation in the same thread by sending
23 | // `threadId` from the second message onwards.
24 | const response2 = await langbase.pipes.run({
25 | name: 'summary',
26 | stream: true,
27 | threadId: response1.threadId!,
28 | messages: [{role: 'user', content: 'Tell me the name of my company?'}],
29 | });
30 |
31 | const runner2 = getRunner(response2.stream);
32 |
33 | runner2.on('content', content => {
34 | process.stdout.write(content);
35 | });
36 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.run.stream.llmkey.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {getRunner, Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const userMsg = 'Who is an AI Engineer?';
10 |
11 | // Get readable stream
12 | const {stream, threadId, rawResponse} = await langbase.pipes.run({
13 | messages: [{role: 'user', content: userMsg}],
14 | stream: true,
15 | rawResponse: true,
16 | name: 'summary',
17 | llmKey: process.env.LLM_KEY!, // Your LLM API key
18 | });
19 |
20 | // Convert the stream to a stream runner.
21 | const runner = getRunner(stream);
22 |
23 | // Method 1: Using event listeners
24 | runner.on('connect', () => {
25 | console.log('Stream started.\n');
26 | });
27 |
28 | runner.on('content', content => {
29 | process.stdout.write(content);
30 | });
31 |
32 | runner.on('end', () => {
33 | console.log('\nStream ended.');
34 | });
35 |
36 | runner.on('error', error => {
37 | console.error('Error:', error);
38 | });
39 | }
40 |
41 | main();
42 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.run.stream.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {getRunner, Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const userMsg = 'Who is an AI Engineer?';
10 |
11 | // Get readable stream
12 | const {stream, threadId, rawResponse} = await langbase.pipes.run({
13 | messages: [{role: 'user', content: userMsg}],
14 | stream: true,
15 | rawResponse: true,
16 | name: 'summary',
17 | });
18 |
19 | console.log('Thread ID:', threadId);
20 | console.log('');
21 |
22 | // Convert the stream to a stream runner.
23 | const runner = getRunner(stream);
24 |
25 | // Method 1: Using event listeners
26 | runner.on('connect', () => {
27 | console.log('Stream started.\n');
28 | });
29 |
30 | runner.on('content', content => {
31 | process.stdout.write(content);
32 | });
33 |
34 | runner.on('end', () => {
35 | console.log('\nStream ended.');
36 | });
37 |
38 | runner.on('error', error => {
39 | console.error('Error:', error);
40 | });
41 | }
42 |
43 | main();
44 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.run.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const userMsg = 'Who is an AI Engineer?';
10 |
11 | const response = await langbase.pipes.run({
12 | messages: [
13 | {
14 | role: 'user',
15 | content: userMsg,
16 | },
17 | ],
18 | stream: false,
19 | name: 'email-sentiment',
20 | });
21 | console.log('response: ', response);
22 | }
23 |
24 | main();
25 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.structured.outputs.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 | import {z} from 'zod';
4 | import {zodToJsonSchema} from 'zod-to-json-schema';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | // Define the Strucutred Output JSON schema with Zod
11 | const MathReasoningSchema = z.object({
12 | steps: z.array(
13 | z.object({
14 | explanation: z.string(),
15 | output: z.string(),
16 | }),
17 | ),
18 | final_answer: z.string(),
19 | });
20 |
21 | const jsonSchema = zodToJsonSchema(MathReasoningSchema, {target: 'openAi'});
22 |
23 | async function createMathTutorPipe() {
24 | const pipe = await langbase.pipes.create({
25 | name: 'math-tutor',
26 | model: 'openai:gpt-4o',
27 | messages: [
28 | {
29 | role: 'system',
30 | content:
31 | 'You are a helpful math tutor. Guide the user through the solution step by step.',
32 | },
33 | ],
34 | json: true,
35 | response_format: {
36 | type: 'json_schema',
37 | json_schema: {
38 | name: 'math_reasoning',
39 | schema: jsonSchema,
40 | },
41 | },
42 | });
43 |
44 | console.log('✅ Math Tutor pipe created:', pipe);
45 | }
46 |
47 | async function runMathTutorPipe(question: string) {
48 | const {completion} = await langbase.pipes.run({
49 | name: 'math-tutor',
50 | messages: [{role: 'user', content: question}],
51 | stream: false,
52 | });
53 |
54 | // Parse and validate the response using Zod
55 | const solution = MathReasoningSchema.parse(JSON.parse(completion));
56 |
57 | console.log('✅ Structured Output Response:', solution);
58 | }
59 |
60 | async function main() {
61 | if (!process.env.LANGBASE_API_KEY) {
62 | console.error('❌ Missing LANGBASE_API_KEY in environment variables.');
63 | process.exit(1);
64 | }
65 |
66 | // Run this only once to create the pipe. Uncomment if it's your first time setting it up.
67 | // await createMathTutorPipe();
68 | await runMathTutorPipe('How can I solve 8x + 22 = -23?');
69 | }
70 |
71 | main();
72 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.tool.stream.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase, getToolsFromRunStream} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const userMsg = "What's the weather in SF";
10 |
11 | const response = await langbase.pipes.run({
12 | messages: [
13 | {
14 | role: 'user',
15 | content: userMsg,
16 | },
17 | ],
18 | stream: true,
19 | name: 'summary',
20 | tools: [
21 | {
22 | type: 'function',
23 | function: {
24 | name: 'get_current_weather',
25 | description: 'Get the current weather of a given location',
26 | parameters: {
27 | type: 'object',
28 | required: ['location'],
29 | properties: {
30 | unit: {
31 | enum: ['celsius', 'fahrenheit'],
32 | type: 'string',
33 | },
34 | location: {
35 | type: 'string',
36 | description:
37 | 'The city and state, e.g. San Francisco, CA',
38 | },
39 | },
40 | },
41 | },
42 | },
43 | ],
44 | });
45 |
46 | const [streamForResponse, streamForToolCall] = response.stream.tee();
47 |
48 | const toolCalls = await getToolsFromRunStream(streamForToolCall);
49 | const hasToolCalls = toolCalls.length > 0;
50 |
51 | if (hasToolCalls) {
52 | // handle the tool calls
53 | console.log('Tools:', toolCalls);
54 | } else {
55 | // handle the response stream
56 | }
57 | }
58 |
59 | main();
60 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.tool.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase, getToolsFromRun} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const userMsg = "What's the weather in SF";
10 |
11 | const response = await langbase.pipes.run({
12 | messages: [
13 | {
14 | role: 'user',
15 | content: userMsg,
16 | },
17 | ],
18 | stream: false,
19 | name: 'summary',
20 | tools: [
21 | {
22 | type: 'function',
23 | function: {
24 | name: 'get_current_weather',
25 | description: 'Get the current weather of a given location',
26 | parameters: {
27 | type: 'object',
28 | required: ['location'],
29 | properties: {
30 | unit: {
31 | enum: ['celsius', 'fahrenheit'],
32 | type: 'string',
33 | },
34 | location: {
35 | type: 'string',
36 | description:
37 | 'The city and state, e.g. San Francisco, CA',
38 | },
39 | },
40 | },
41 | },
42 | },
43 | ],
44 | });
45 |
46 | const toolCalls = await getToolsFromRun(response);
47 | const hasToolCalls = toolCalls.length > 0;
48 |
49 | if (hasToolCalls) {
50 | // handle the tool calls
51 | console.log('Tools:', toolCalls);
52 | } else {
53 | // handle the response
54 | console.log('Response:', response);
55 | }
56 | }
57 |
58 | main();
59 |
--------------------------------------------------------------------------------
/examples/nodejs/pipes/pipe.update.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Langbase} from 'langbase';
3 |
4 | const langbase = new Langbase({
5 | apiKey: process.env.LANGBASE_API_KEY!,
6 | });
7 |
8 | async function main() {
9 | const response = await langbase.pipes.update({
10 | name: 'summary',
11 | description: 'This is a pipe updated with the SDK',
12 | model: 'google:gemini-1.5-flash-8b-latest',
13 | });
14 |
15 | console.log(response);
16 | }
17 |
18 | main();
19 |
--------------------------------------------------------------------------------
/examples/nodejs/readme.md:
--------------------------------------------------------------------------------
1 | # Node.js Examples
2 |
3 | Langbase Node.js examples for the Pipe API.
4 |
5 | ```sh
6 | # Make sure to copy .env.example file and create .env file and add all the Pipe API keys in it
7 | cp .env.example .env
8 |
9 | # Then test any of the files or a script which runs these files.
10 |
11 | # pipes
12 | npm run pipe.run
13 | npm run pipe.run.stream
14 | npm run pipe.run.chat
15 | npm run pipe.run.stream.chat
16 | npm run pipe.run.stream.llmkey
17 | npm run pipe.run.pipe.key
18 | npm run pipe.list
19 | npm run pipe.create
20 | npm run pipe.update
21 | npm run pipe.tool
22 | npm run pipe.tool.stream
23 |
24 | # memory
25 | npm run memory.list
26 | npm run memory.create
27 | npm run memory.delete
28 | npm run memory.retrieve
29 |
30 | # docs
31 | npm run memory.docs.list
32 | npm run memory.docs.upload
33 | npm run memory.docs.delete
34 | npm run memory.docs.retry-embed
35 |
36 | # tools
37 | npm run tools.web-search
38 | npm run tools.crawl
39 |
40 | # chunk
41 | npm run chunk
42 |
43 | # embed
44 | npm run embed
45 |
46 | # parse
47 | npm run parse
48 |
49 | # threads
50 | npm run thread.messages.add
51 | npm run thread.messages.list
52 | npm run thread.delete
53 | ```
54 |
--------------------------------------------------------------------------------
/examples/nodejs/threads/threads.append.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const response = await langbase.threads.append({
12 | threadId: 'REPLACE_WITH_THREAD_ID',
13 | messages: [
14 | {
15 | role: 'assistant',
16 | content: 'Nice to meet you',
17 | metadata: {size: 'small'},
18 | },
19 | ],
20 | });
21 |
22 | console.log(response);
23 | }
24 |
25 | main();
26 |
--------------------------------------------------------------------------------
/examples/nodejs/threads/threads.create.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const response = await langbase.threads.create({
12 | messages: [{role: 'user', content: 'hello', metadata: {size: 'small'}}],
13 | metadata: {company: 'langbase'},
14 | });
15 |
16 | console.log(response);
17 | }
18 |
19 | main();
20 |
--------------------------------------------------------------------------------
/examples/nodejs/threads/threads.delete.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const response = await langbase.threads.delete({
12 | threadId: 'REPLACE_WITH_THREAD_ID',
13 | });
14 |
15 | console.log(response);
16 | }
17 |
18 | main();
19 |
--------------------------------------------------------------------------------
/examples/nodejs/threads/threads.get.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const response = await langbase.threads.get({
12 | threadId: 'REPLACE_WITH_THREAD_ID',
13 | });
14 |
15 | console.log(response);
16 | }
17 |
18 | main();
19 |
--------------------------------------------------------------------------------
/examples/nodejs/threads/threads.messages.list.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const response = await langbase.threads.messages.list({
12 | threadId: 'REPLACE_WITH_THREAD_ID',
13 | });
14 |
15 | console.log(response);
16 | }
17 |
18 | main();
19 |
--------------------------------------------------------------------------------
/examples/nodejs/threads/threads.update.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | async function main() {
11 | const response = await langbase.threads.update({
12 | threadId: 'REPLACE_WITH_THREAD_ID',
13 | metadata: {
14 | company: 'langbase',
15 | about: 'Langbase is the most powerful serverless platform for building AI agents with memory.',
16 | },
17 | });
18 |
19 | console.log(response);
20 | }
21 |
22 | main();
23 |
--------------------------------------------------------------------------------
/examples/nodejs/tools/crawl.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | const langbase = new Langbase({
7 | apiKey: process.env.LANGBASE_API_KEY!,
8 | });
9 |
10 | /**
11 | * Crawls specified URLs using spider.cloud service.
12 | *
13 | * Get your API key from the following link and set it in .env file.
14 | *
15 | * @link https://spider.cloud/docs/quickstart
16 | */
17 | async function main() {
18 | const results = await langbase.tools.crawl({
19 | url: ['https://langbase.com', 'https://langbase.com/about'],
20 | maxPages: 1,
21 | apiKey: process.env.CRAWL_KEY,
22 | });
23 |
24 | console.log(results);
25 | }
26 |
27 | main();
28 |
--------------------------------------------------------------------------------
/examples/nodejs/tools/web-search.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 | import 'dotenv/config';
4 | import {Langbase} from 'langbase';
5 |
6 | // Learn more about Langbase API keys: https://langbase.com/docs/api-reference/api-keys
7 | const langbase = new Langbase({
8 | apiKey: process.env.LANGBASE_API_KEY!,
9 | });
10 |
11 | async function main() {
12 | const results = await langbase.tools.webSearch({
13 | service: 'exa',
14 | totalResults: 2,
15 | query: 'What is Langbase?',
16 | domains: ['https://langbase.com'],
17 | apiKey: process.env.EXA_API_KEY!, // Find Exa key: https://dashboard.exa.ai/api-keys
18 | });
19 |
20 | console.log(results);
21 | }
22 |
23 | main();
24 |
--------------------------------------------------------------------------------
/examples/nodejs/workflows/workflows.ts:
--------------------------------------------------------------------------------
1 | // Experimental upcoming beta AI primitve.
2 | // Please refer to the documentation for more information: https://langbase.com/docs for more information.
3 |
4 | import 'dotenv/config';
5 | import {Langbase, Workflow} from 'langbase';
6 |
7 | const langbase = new Langbase({
8 | apiKey: process.env.LANGBASE_API_KEY!,
9 | });
10 |
11 | async function main() {
12 | const {step} = new Workflow({debug: true});
13 |
14 | const result = await step({
15 | id: 'sumamrize',
16 | run: async () => {
17 | return langbase.llm.run({
18 | model: 'openai:gpt-4o-mini',
19 | apiKey: process.env.OPENAI_API_KEY!,
20 | messages: [
21 | {
22 | role: 'system',
23 | content:
24 | 'You are an expert summarizer. Summarize the user input.',
25 | },
26 | {
27 | role: 'user',
28 | content:
29 | 'I am testing workflows. I just created an example of summarize workflow. Can you summarize this?',
30 | },
31 | ],
32 | stream: false,
33 | });
34 | },
35 | });
36 |
37 | console.log(result['completion']);
38 | }
39 |
40 | main();
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "langbase",
4 | "license": "Apache-2.0",
5 | "scripts": {
6 | "dev": "turbo dev",
7 | "dev:pkgs": "turbo run dev --filter=./packages/*",
8 | "prepare": "husky",
9 | "lint": "turbo lint",
10 | "test": "turbo test",
11 | "build:pkgs": "turbo run build --filter=./packages/*",
12 | "changeset": "changeset",
13 | "publint": "turbo publint",
14 | "type-check": "turbo type-check",
15 | "version-packages": "changeset version && pnpm clean-examples && pnpm install --no-frozen-lockfile",
16 | "clean": "turbo clean",
17 | "clean-all": "turbo clean && rm -rf node_modules",
18 | "format": "prettier --write \"**/*.{ts,tsx,md}\"",
19 | "release": "turbo clean && pnpm install && turbo run build --filter=./packages/* && changeset publish",
20 | "releaseDocs": "turbo build --filter=docs^... && changeset publish",
21 | "releasex": "turbo build && changeset publish",
22 | "prettier-check": "prettier --check \"**/*.{js,ts,tsx,md,mdx}\"",
23 | "prettier-fix": "prettier --write \"**/*.{js,ts,tsx,md,mdx}\"",
24 | "devv": "turbo dev --no-cache --concurrency 16 --continue",
25 | "ci:release": "turbo clean && turbo build && changeset publish",
26 | "clean-examples": "node .github/scripts/cleanup-examples-changesets.mjs",
27 | "ci:version": "changeset version && node .github/scripts/cleanup-examples-changesets.mjs && pnpm install --no-frozen-lockfile",
28 | "snapshot": "node .github/scripts/release-snapshot.js",
29 | "update-examples": "npx tsx .github/scripts/update-examples.ts"
30 | },
31 | "devDependencies": {
32 | "@changesets/cli": "^2.27.5",
33 | "@langbase/eslint-config": "workspace:*",
34 | "@types/node": "^20.11.24",
35 | "eslint": "^8.57.0",
36 | "husky": "^9.0.11",
37 | "lint-staged": "^15.2.7",
38 | "prettier": "^3.3.2",
39 | "publint": "^0.2.8",
40 | "turbo": "^2.1.2",
41 | "vitest": "^1.6.0"
42 | },
43 | "packageManager": "pnpm@9.4.0",
44 | "engines": {
45 | "node": ">=18"
46 | },
47 | "lint-staged": {
48 | "*": [
49 | "prettier --ignore-unknown --write"
50 | ]
51 | },
52 | "homepage": "https://langbase.com/docs",
53 | "repository": {
54 | "type": "git",
55 | "url": "git+https://github.com/LangbaseInc/langbase-sdk.git"
56 | },
57 | "bugs": {
58 | "url": "https://github.com/LangbaseInc/langbase-sdk/issues"
59 | },
60 | "keywords": [
61 | "ai",
62 | "llm",
63 | "baseai",
64 | "base ai",
65 | "langbase",
66 | "langbase.com",
67 | "generative AI"
68 | ]
69 | }
70 |
--------------------------------------------------------------------------------
/packages/cli/.gitignore:
--------------------------------------------------------------------------------
1 | # NPM #
2 | ##########
3 | # Ignore all directories called node_modules in current folder and any subfolders.
4 | node_modules/
5 | /node_modules/
6 |
7 | # Packages #
8 | ############
9 | *.7z
10 | *.dmg
11 | *.gz
12 | *.bz2
13 | *.iso
14 | *.jar
15 | *.rar
16 | *.tar
17 | *.zip
18 | *.tgz
19 | *.map
20 |
21 | # Logs and databases #
22 | ######################
23 | *.log
24 | *.sql
25 | *.env
26 |
27 | # OS generated files #
28 | ######################
29 | **.DS_Store*
30 | ehthumbs.db
31 | Icon?
32 | Thumbs.db
33 | ._*
34 | **settings.dat*
35 |
36 | # Vim generated files #
37 | ######################
38 | *.un~
39 |
40 | # SASS #
41 | ##########
42 | **/.sass-cache
43 | **/.sass-cache/*
44 | **/.map
45 |
46 | # Composer #
47 | ##########
48 | !assets/js/vendor/
49 | wpcs/
50 | /vendor/
51 |
52 | # Bower #
53 | ##########
54 | assets/bower_components/*
55 |
56 | # Codekit #
57 | ##########
58 | /codekit-config.json
59 | *.codekit
60 | **.codekit-cache/*
61 |
62 | # Compiled Files and Build Dirs #
63 | ##########
64 | /README.html
65 |
66 | # PhpStrom Project Files #
67 | .idea/
68 | library/vendors/composer
69 | assets/img/.DS_Store
70 |
71 | # VSCode related files #
72 | # .vscode
73 |
--------------------------------------------------------------------------------
/packages/cli/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "none",
3 | "arrowParens": "avoid",
4 | "singleQuote": true,
5 | "printWidth": 80,
6 | "useTabs": true,
7 | "tabWidth": 4,
8 | "semi": true
9 | }
10 |
--------------------------------------------------------------------------------
/packages/cli/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @langbase/cli
2 |
3 | ## 0.1.5
4 |
5 | ### Patch Changes
6 |
7 | - 👌 IMPROVE: Langbase CLI
8 |
9 | ## 0.1.4
10 |
11 | ### Patch Changes
12 |
13 | - 3619dad: 📦 NEW: CLI command
14 | - 📦 NEW: Add CLI commands
15 |
16 | ## 0.1.3
17 |
18 | ### Patch Changes
19 |
20 | - 📦 NEW: Langbase Docs MCP server
21 |
22 | ## 0.1.2
23 |
24 | ### Patch Changes
25 |
26 | - 👌 IMPROVE: make pkg public
27 |
28 | ## 0.1.1
29 |
30 | ### Patch Changes
31 |
32 | - 👌 IMPROVE: lingo and readme
33 |
34 | ## 0.1.0
35 |
36 | ### Minor Changes
37 |
38 | - 📦 NEW: AH Langbase CLI
39 |
--------------------------------------------------------------------------------
/packages/cli/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We welcome contributions to this project.
4 |
5 | ---
6 |
7 | ## Releasing a snapshot version
8 |
9 | To release a snapshot version to test changes, run the following command:
10 |
11 | ```bash
12 | npx run snapshot
13 | ```
14 |
15 | ## Releasing a new version
16 |
17 | ```bash
18 | pnpm changeset
19 | pnpm version-packages
20 | grlz 'new version'
21 | pnpm release
22 | pnpm update-examples
23 | ```
24 |
25 | ## Testing locally
26 |
27 | To test the changes locally, you can run the following command:
28 |
29 | - Navigate to an example's folder like the Node.js one in `examples/nodejs`.
30 |
31 | - Change the `package.json` to point to the local package for `@langbase/cli` package.
32 |
33 | ```json
34 | {
35 | "devDependencies": {
36 | "@langbase/cli": "workspace:*"
37 | }
38 | }
39 | ```
40 |
41 | - Now run in the root:
42 |
43 | ```bash
44 | pnpm clean-all && pnpm install
45 | ```
46 |
47 | Then run the development server:
48 |
49 | ```bash
50 | pnpm dev
51 | ```
52 |
53 | - Run the Node.js example:
54 |
55 | ```bash
56 | # 1. Authenticate first
57 | npx lb auth
58 |
59 | # 2. Deploy the agent
60 | npx lb deploy --agent owner/agentName --file ./src/index.ts
61 | ```
62 |
63 | By doing this, the Node.js example will use the local packages instead of the published ones.
64 |
--------------------------------------------------------------------------------
/packages/cli/README.md:
--------------------------------------------------------------------------------
1 | # Langbase CLI
2 |
3 | The Langbase CLI is a command-line interface for Langbase. It provides a set of commands to interact with the Langbase SDK and perform various tasks related to AI development.
4 |
5 | ## Usage
6 | ### Langbase Docs MCP Server
7 |
8 | Integrate the Langbase Docs MCP server into your IDEs and Claude Desktop.
9 |
10 | #### Cursor
11 | - Open Cursor settings
12 | - Navigate to the MCP settings
13 | - Click on the `+` button to add a new global MCP server
14 | - Paste the following configuration in the `mcp.json` file:
15 | ```json
16 | {
17 | "mcpServers": {
18 | "Langbase": {
19 | "command": "npx",
20 | "args": ["@langbase/cli","docs-mcp-server"]
21 | }
22 | }
23 | }
24 | ```
25 |
26 | #### Windsurf
27 | - Navigate to Windsurf - Settings > Advanced Settings
28 | - You will find the option to Add Server
29 | - Click “Add custom server +”
30 | - Paste the following configuration in the `mcp_config.json` file:
31 | ```json
32 | {
33 | "mcpServers": {
34 | "Langbase": {
35 | "command": "npx",
36 | "args": ["@langbase/cli", "docs-mcp-server"]
37 | }
38 | }
39 | }
40 | ```
41 |
42 | #### Claude Desktop
43 | - Open Claude Desktop File Menu
44 | - Navigate to the settings
45 | - Go to Developer Tab
46 | - Click on the Edit Config button
47 | - Paste the following configuration in the `claude_desktop_config.json` file:
48 | ```json
49 | {
50 | "mcpServers": {
51 | "Langbase": {
52 | "command": "npx",
53 | "args": ["@langbase/cli", "docs-mcp-server"]
54 | }
55 | }
56 | }
57 | ```
58 |
59 | ## Next steps
60 |
61 | - Read the [Langbase SDK documentation](https://langbase.com/docs/sdk) for more details
62 | - Join our [Discord](https://langbase.com/discord) community for feedback, requests, and support
63 |
--------------------------------------------------------------------------------
/packages/cli/bin/langbase.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { install } from 'source-map-support';
4 | import { fileURLToPath } from 'url';
5 |
6 | install();
7 |
8 | const __filename = fileURLToPath(import.meta.url);
9 | const distPath = new URL('../dist/index.js', import.meta.url).pathname;
10 |
11 | import(distPath);
12 |
--------------------------------------------------------------------------------
/packages/cli/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@langbase/cli",
3 | "description": "Langbase CLI",
4 | "version": "0.1.5",
5 | "license": "UNLICENSED",
6 | "type": "module",
7 | "main": "./dist/index.js",
8 | "module": "./dist/index.mjs",
9 | "types": "./dist/index.d.ts",
10 | "bin": {
11 | "langbase": "dist/index.js",
12 | "lb": "dist/index.js"
13 | },
14 | "author": {
15 | "name": "Langbase",
16 | "url": "https://langbase.com"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/LangbaseInc/components.git",
21 | "directory": "packages/cli"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/LangbaseInc/components/issues"
25 | },
26 | "homepage": "https://langbase.com",
27 | "files": [
28 | "dist/**"
29 | ],
30 | "scripts": {
31 | "build": "tsup",
32 | "dev": "tsup --watch",
33 | "lint": "eslint \"src/**/*.ts*\"",
34 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
35 | "type-check": "tsc --noEmit",
36 | "prettier-check": "prettier --check \"./**/*.ts*\"",
37 | "test": "pnpm test:node && pnpm test:edge",
38 | "#test": "pnpm test:node && pnpm test:edge && pnpm test:ui && pnpm test:e2e",
39 | "test:edge": "vitest --config vitest.edge.config.js --run",
40 | "test:node": "vitest --config vitest.node.config.js --run",
41 | "test:ui": "pnpm test:ui:react",
42 | "test:ui:react": "vitest --config vitest.ui.react.config.js --run",
43 | "test:e2e": "playwright test",
44 | "test:edge:watch": "vitest --config vitest.edge.config.js",
45 | "test:node:watch": "vitest --config vitest.node.config.js",
46 | "test:ui:react:watch": "vitest --config vitest.ui.react.config.js"
47 | },
48 | "dependencies": {
49 | "@antfu/ni": "^0.23.0",
50 | "@clack/core": "^0.3.4",
51 | "@clack/prompts": "^0.7.0",
52 | "@hono/node-server": "^1.13.1",
53 | "@hono/zod-openapi": "^0.16.0",
54 | "@modelcontextprotocol/sdk": "^1.8.0",
55 | "@sindresorhus/slugify": "^2.2.1",
56 | "camelcase": "^8.0.0",
57 | "chalk": "^5.3.0",
58 | "cli-alerts": "^2.0.0",
59 | "cli-handle-error": "^4.4.0",
60 | "cli-handle-unhandled": "^1.1.1",
61 | "cli-meow-help": "^4.0.0",
62 | "cli-table3": "^0.6.5",
63 | "cli-welcome": "^3.0.0",
64 | "compute-cosine-similarity": "^1.1.0",
65 | "cosmiconfig": "^9.0.0",
66 | "cosmiconfig-typescript-loader": "^5.0.0",
67 | "dotenv": "^16.4.5",
68 | "esbuild": "^0.24.2",
69 | "execa": "^9.4.0",
70 | "fast-glob": "^3.3.2",
71 | "figures": "^6.1.0",
72 | "get-package-json-file": "^2.0.0",
73 | "hono": "^4.5.11",
74 | "js-tiktoken": "^1.0.14",
75 | "jsdom": "^24.1.0",
76 | "log-symbols": "^7.0.0",
77 | "lowdb": "^7.0.1",
78 | "meow": "^13.2.0",
79 | "node-fetch": "^3.3.2",
80 | "open": "^10.1.0",
81 | "openai": "^4.63.0",
82 | "p-map": "^7.0.2",
83 | "picocolors": "^1.1.0",
84 | "prettier": "^3.3.3",
85 | "source-map-support": "^0.5.21",
86 | "unpdf": "^0.11.0",
87 | "uuid": "^10.0.0",
88 | "xlsx": "^0.18.5",
89 | "zod": "^3.23.8",
90 | "zod-error": "^1.5.0"
91 | },
92 | "devDependencies": {
93 | "@langbase/eslint-config": "workspace:*",
94 | "@langbase/tsconfig": "workspace:*",
95 | "@types/jsdom": "^21.1.7",
96 | "@types/node": "^22.6.1",
97 | "tsup": "^8.3.0",
98 | "tsx": "^4.19.1",
99 | "typescript": "^5.6.2",
100 | "vitest": "1.6.0"
101 | },
102 | "publishConfig": {
103 | "access": "public"
104 | },
105 | "keywords": [
106 | "langbase",
107 | "langbase CLI",
108 | "langbase.com",
109 | "generative AI"
110 | ]
111 | }
112 |
--------------------------------------------------------------------------------
/packages/cli/src/auth/index.ts:
--------------------------------------------------------------------------------
1 | import { findEnvFile } from '@/utils/find-env';
2 | import { heading } from '@/utils/heading';
3 | import * as p from '@clack/prompts';
4 | import {
5 | cancel,
6 | confirm,
7 | isCancel,
8 | note,
9 | outro,
10 | password
11 | } from '@clack/prompts';
12 | import fs from 'fs/promises';
13 | import open from 'open';
14 | import path from 'path';
15 | import color from 'picocolors';
16 |
17 | export async function auth() {
18 | p.intro(
19 | heading({
20 | text: 'Langbase Authentication',
21 | sub: 'Auth by logging in to Langbase and getting your API key'
22 | })
23 | );
24 |
25 | const shouldOpen = await confirm({
26 | message: `Open the authentication page? ${color.dim(`— copy your API key from there and paste it here.`)}`
27 | });
28 |
29 | if (isCancel(shouldOpen)) {
30 | cancel('Operation cancelled.');
31 | process.exit(0);
32 | }
33 |
34 | if (shouldOpen) {
35 | await open('https://langbase.com/settings/api');
36 |
37 | note(
38 | color.yellow(
39 | 'Please copy your API key from the opened page and paste it here.'
40 | )
41 | );
42 | }
43 |
44 | const apiKeyString = await password({
45 | message: 'Paste your API key string:',
46 | mask: '*'
47 | });
48 |
49 | if (isCancel(apiKeyString)) {
50 | cancel('Operation cancelled.');
51 | process.exit(0);
52 | }
53 |
54 | const [login, apiKey] = (apiKeyString as string).split(':');
55 |
56 | if (!login || !apiKey) {
57 | outro(
58 | color.red(
59 | 'Invalid API key string. It should be in the format login:apiKey, when copied from https://langbase.com/settings/api it should be in the correct format.'
60 | )
61 | );
62 | process.exit(1);
63 | }
64 |
65 | const envKeyName = 'LANGBASE_API_KEY';
66 | const envContent = `\n# Langbase API key for https://langbase.com/${login}\n${envKeyName}=${apiKey}\n\n`;
67 |
68 | let envFile = await findEnvFile();
69 |
70 | if (!envFile) {
71 | envFile = '.env';
72 | await fs.writeFile(path.join(process.cwd(), envFile), envContent);
73 | } else {
74 | const envFileContent = await fs.readFile(envFile, 'utf-8');
75 |
76 | const oldKey = envFileContent
77 | .split('\n')
78 | .reverse() // Reverse to get the latest key if there are multiple
79 | .find(line => line.includes('LANGBASE_API_KEY'))
80 | ?.split('=')[1];
81 | if (oldKey) {
82 | const shouldOverwrite = await confirm({
83 | message: `API key found in ${envFile}. Overwrite?`
84 | });
85 |
86 | if (isCancel(shouldOverwrite)) {
87 | cancel('Operation cancelled.');
88 | process.exit(0);
89 | }
90 |
91 | if (!shouldOverwrite) {
92 | outro(color.yellow('API key is not overwritten.'));
93 | process.exit(0);
94 | }
95 |
96 | const newEnvContent = envFileContent.replace(
97 | new RegExp(`LANGBASE_API_KEY=${oldKey}`),
98 | envContent.trim()
99 | );
100 |
101 | await fs.writeFile(
102 | path.join(process.cwd(), envFile),
103 | newEnvContent
104 | );
105 | } else {
106 | await fs.appendFile(path.join(process.cwd(), envFile), envContent);
107 | }
108 | }
109 |
110 | outro(
111 | color.green(
112 | `Authentication successful. API key is stored in ${envFile}`
113 | )
114 | );
115 | process.exit(0);
116 | }
117 |
--------------------------------------------------------------------------------
/packages/cli/src/build/index.ts:
--------------------------------------------------------------------------------
1 | import { buildCode } from '@/utils/build';
2 | import { color } from '@/utils/formatting';
3 | import { handleError } from '@/utils/handle-error';
4 | import { heading } from '@/utils/heading';
5 | import { buildOptionsSchema } from '@/utils/schema';
6 | import * as p from '@clack/prompts';
7 | import { outro } from '@clack/prompts';
8 |
9 | type BuildOptions = {
10 | filePath: string;
11 | };
12 |
13 | export async function build(options: BuildOptions) {
14 | const spinner = p.spinner();
15 | try {
16 | p.intro(heading({ text: 'BUILD', sub: 'Building code' }));
17 |
18 | // 1. Validate options
19 | const validatedOptions = validateOptions(options);
20 |
21 | // 2. Build code
22 | const { distPath } = await buildCode({
23 | spinner,
24 | filePath: validatedOptions.filePath
25 | });
26 |
27 | // 3. Output success message
28 | outro(
29 | color.green(`Built code is stored in ${distPath}/langbase.build.js`)
30 | );
31 | } catch (error) {
32 | handleError({
33 | spinner,
34 | message: 'An unexpected error occurred',
35 | error
36 | });
37 | }
38 | }
39 |
40 | function validateOptions(options: BuildOptions) {
41 | const {
42 | data: deployOptions,
43 | success,
44 | error
45 | } = buildOptionsSchema.safeParse(options);
46 | if (!success) {
47 | const errorMessages = error.errors.map((error: any) => {
48 | return error.message;
49 | });
50 |
51 | const errorMessage = errorMessages.join('. ');
52 |
53 | p.cancel(`Invalid options: ${errorMessage}`);
54 | process.exit(1);
55 | }
56 |
57 | return {
58 | filePath: deployOptions.filePath
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/packages/cli/src/data/constants.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LangbaseInc/langbase-sdk/32acf46cf307910adca31f105f6bdd85c9faba51/packages/cli/src/data/constants.ts
--------------------------------------------------------------------------------
/packages/cli/src/data/index.ts:
--------------------------------------------------------------------------------
1 | export function getWorkerTemplaleCode(src: string) {
2 | const code = `import api from '${src}';
3 |
4 | export default {
5 | fetch(request, env) {
6 | return api(request, env);
7 | }
8 | }`;
9 |
10 | return code;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/cli/src/deploy/index.ts:
--------------------------------------------------------------------------------
1 | import { buildCode } from '@/utils/build';
2 | import { getBaseUrl } from '@/utils/get-base-url';
3 | import fs from 'fs/promises';
4 | import path from 'path';
5 | import * as p from '@clack/prompts';
6 | import { heading } from '@/utils/heading';
7 | import { retrieveAuthentication } from '@/utils/retrieve-credentials';
8 | import slugify from '@sindresorhus/slugify';
9 | import { z } from 'zod';
10 | import { handleError } from '@/utils/handle-error';
11 | import { Spinner } from 'types';
12 | import { deployOptionsSchema } from '@/utils/schema';
13 |
14 | type DeployOptions = {
15 | isDev: boolean;
16 | agent: string;
17 | filePath: string;
18 | apiKey: string;
19 | };
20 |
21 | export async function deploy(options: DeployOptions) {
22 | const spinner = p.spinner();
23 | try {
24 | p.intro(heading({ text: 'DEPLOY', sub: 'Deploying to Langbase' }));
25 |
26 | // 1. Validate options
27 | const validatedOptions = validateOptions(options);
28 |
29 | // 2. Build code
30 | const { distPath } = await buildCode({
31 | spinner,
32 | filePath: validatedOptions.filePath
33 | });
34 |
35 | // 3. Deploy code
36 | await deployCode({ ...validatedOptions, distPath, spinner });
37 | } catch (error) {
38 | handleError({
39 | spinner,
40 | message: 'An unexpected error occurred',
41 | error
42 | });
43 | }
44 | }
45 |
46 | async function deployCode({
47 | isDev,
48 | distPath,
49 | owner,
50 | agent,
51 | filePath,
52 | spinner,
53 | apiKey: lbApiKey
54 | }: {
55 | isDev: boolean;
56 | distPath: string;
57 | owner: string;
58 | agent: string;
59 | filePath: string;
60 | spinner: Spinner;
61 | apiKey?: string;
62 | }) {
63 | try {
64 | const scriptName = path.basename(filePath).split('.')[0];
65 | const slugifiedScripptName = slugify(scriptName);
66 |
67 | // 1. Get the deploy API endpoint
68 | const endpoint = await getDeployApiEndpoint({
69 | isDev,
70 | agent,
71 | owner,
72 | scriptName: slugifiedScripptName
73 | });
74 |
75 | // 2. Get the deploy API request options
76 | const { body, apiKey } = await getDeployApiRequestOptions({
77 | distPath,
78 | spinner,
79 | lbApiKey,
80 | scriptName: slugifiedScripptName
81 | });
82 |
83 | console.log('apiKey', apiKey);
84 | console.log('body', body);
85 |
86 | // 3. Request the deploy API
87 | await requestDeployApi({
88 | body,
89 | apiKey,
90 | spinner,
91 | endpoint
92 | });
93 | } catch (error) {
94 | p.cancel(`Error deploying code: ${error}`);
95 | process.exit(1);
96 | }
97 | }
98 |
99 | function validateOptions(options: DeployOptions) {
100 | const {
101 | data: deployOptions,
102 | success,
103 | error
104 | } = deployOptionsSchema.safeParse(options);
105 | if (!success) {
106 | const errorMessages = error.errors.map((error: any) => {
107 | return error.message;
108 | });
109 |
110 | const errorMessage = errorMessages.join('. ');
111 |
112 | p.cancel(`Invalid options: ${errorMessage}`);
113 | process.exit(1);
114 | }
115 |
116 | const [owner, agent] = deployOptions.agent.split('/');
117 |
118 | return {
119 | owner,
120 | agent,
121 | isDev: deployOptions.isDev,
122 | apiKey: deployOptions.apiKey,
123 | filePath: deployOptions.filePath
124 | };
125 | }
126 |
127 | async function getDeployApiEndpoint({
128 | isDev,
129 | agent,
130 | owner,
131 | scriptName
132 | }: {
133 | isDev: boolean;
134 | agent: string;
135 | owner: string;
136 | scriptName: string;
137 | }) {
138 | const baseUrl = getBaseUrl(isDev);
139 | const slugifiedAgent = slugify(agent);
140 | const slugifiedScriptName = slugify(scriptName);
141 | return `${baseUrl}/v1/${owner}/${slugifiedAgent}/${slugifiedScriptName}/deploy`;
142 | }
143 |
144 | async function getDeployApiRequestOptions({
145 | spinner,
146 | distPath,
147 | scriptName,
148 | lbApiKey
149 | }: {
150 | spinner: Spinner;
151 | distPath: string;
152 | scriptName: string;
153 | lbApiKey?: string;
154 | }) {
155 | // 1. Read the built file
156 | const buildPath = path.join(distPath, 'langbase.build.js');
157 | const file = await fs.readFile(buildPath, 'utf-8');
158 |
159 | // 2. Create a FormData object
160 | const formData = new FormData();
161 |
162 | // 3. Append the script to the FormData object
163 | formData.append(
164 | 'script',
165 | new Blob([file], { type: 'application/javascript' }),
166 | scriptName
167 | );
168 |
169 | // 4. Get the API key
170 | let apiKey = lbApiKey;
171 | if (!apiKey) {
172 | apiKey = await retrieveAuthentication({ spinner });
173 | }
174 |
175 | // 4. Return the FormData object
176 | return { body: formData, apiKey };
177 | }
178 |
179 | async function requestDeployApi({
180 | body,
181 | apiKey,
182 | spinner,
183 | endpoint
184 | }: {
185 | body: FormData;
186 | apiKey: string;
187 | spinner: Spinner;
188 | endpoint: string;
189 | }) {
190 | spinner.start('Deploying code...');
191 | try {
192 | const response = await fetch(endpoint, {
193 | method: 'POST',
194 | body,
195 | headers: {
196 | Authorization: `Bearer ${apiKey}`
197 | }
198 | });
199 |
200 | if (!response.ok) {
201 | const res = await response.json();
202 | spinner.stop('Error deploying code');
203 | p.outro(`Error deploying code: ${res.error.message}`);
204 | process.exit(1);
205 | }
206 |
207 | const res = await response.json();
208 | if (res.success) {
209 | spinner.stop('Code deployed successfully');
210 | }
211 | } catch (error) {
212 | p.cancel(`Error deploying code: ${error}`);
213 | process.exit(1);
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/packages/cli/src/docs-mcp-server/docs.ts:
--------------------------------------------------------------------------------
1 | import { JSDOM } from 'jsdom';
2 |
3 | /**
4 | * Fetches a list of all the docs on the langbase website
5 | *
6 | *
7 | * @returns {Promise} A promise that resolves to a string of all the docs on the langbase website
8 | */
9 | export async function fetchDocsList() {
10 | try {
11 | const response = await fetch('https://langbase.com/docs/llms.txt');
12 | if (!response.ok) {
13 | throw new Error('Failed to fetch docs');
14 | }
15 |
16 | const text = await response.text();
17 | return text;
18 | } catch (error) {
19 | throw new Error('Failed to fetch docs ' + JSON.stringify(error));
20 | }
21 | }
22 |
23 | /**
24 | * Fetches and converts a blog post to markdown
25 | *
26 | *
27 | * @param {string} url - The URL of the blog post to fetch
28 | * @returns {Promise} A promise that resolves to a string of the blog post in markdown format
29 | */
30 | export async function fetchDocsPost(url: string): Promise {
31 | try {
32 | const response = await fetch(url);
33 | if (!response.ok) {
34 | throw new Error('Failed to fetch blog post');
35 | }
36 |
37 | const html = await response.text();
38 |
39 | const dom = new JSDOM(html);
40 | const document = dom.window.document;
41 |
42 | // Remove Next.js initialization code
43 | const scripts = document.querySelectorAll('script');
44 | scripts.forEach(script => script.remove());
45 |
46 | // Get the main content
47 | const content = document.body.textContent?.trim() || '';
48 | if (!content) {
49 | throw new Error('No content found in docs');
50 | }
51 |
52 | return content;
53 | } catch (error) {
54 | throw new Error(
55 | `Failed to fetch docs: ${error instanceof Error ? error.message : 'Something went wrong. Please try again.'}`
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/cli/src/docs-mcp-server/index.ts:
--------------------------------------------------------------------------------
1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3 | import { z } from 'zod';
4 | import { fetchDocsList, fetchDocsPost } from './docs';
5 | import { getRelevanceScore } from '@/utils/get-score';
6 |
7 | export async function docsMcpServer() {
8 | const server = new McpServer({
9 | name: 'langbase-docs-server',
10 | version: '0.1.0'
11 | });
12 |
13 | server.tool(
14 | 'docs-route-finder',
15 | "Searches through all available documentation routes and returns relevant paths based on the user's query. This tool helps navigate the documentation by finding the most appropriate sections that match the search criteria.",
16 | {
17 | query: z.string()
18 | .describe(`A refined search term extracted from the user's question.
19 | For example, if user asks 'How do I create a pipe?', the query would be 'SDK Pipe'.
20 | This should be the specific concept or topic to search for in the documentation.
21 | Treat keyword add as create if user ask for Eg. 'How do I add memory to pipe?' the query should be 'create memory'`)
22 | },
23 | async ({ query }) => {
24 | const docs = await fetchDocsList();
25 | // search through the docs and return the most relevent path based on the query
26 | const docLines = docs.split('\n').filter(line => line.trim());
27 |
28 | // Score and sort the documentation entries
29 | const scoredDocs = docLines
30 | .map(line => ({
31 | line,
32 | score: getRelevanceScore(line, query)
33 | }))
34 | .sort((a, b) => b.score - a.score)
35 | .filter(doc => doc.score > 0)
36 | .slice(0, 3); // Get top 3 most relevant results
37 |
38 | const hasRelevantDocs = scoredDocs.length === 0;
39 |
40 | if (hasRelevantDocs) {
41 | return {
42 | content: [
43 | {
44 | type: 'text',
45 | text:
46 | 'No relevant documentation found for the query: ' +
47 | query
48 | }
49 | ]
50 | };
51 | }
52 |
53 | const results = scoredDocs.map(doc => doc.line).join('\n');
54 |
55 | return {
56 | content: [
57 | {
58 | type: 'text',
59 | text: results
60 | }
61 | ]
62 | };
63 | }
64 | );
65 |
66 | server.tool(
67 | 'sdk-documentation-fetcher',
68 | 'Fetches detailed SDK documentation, specializing in implementation guides for core features like pipes, memory, and tools. This is the primary source for the latest SDK documentation and should be consulted first for questions about creating or implementing SDK components. Use this tool for detailed step-by-step instructions on building pipes, configuring memory systems, and developing custom tools.',
69 | {
70 | url: z
71 | .string()
72 | .describe(
73 | 'URL of a specific SDK page to fetch. Format: /sdk/...'
74 | )
75 | },
76 | async ({ url }) => {
77 | const content = await fetchDocsPost(
78 | `https://langbase.com/docs${url}`
79 | );
80 | return {
81 | content: [
82 | {
83 | type: 'text',
84 | text: content
85 | }
86 | ]
87 | };
88 | }
89 | );
90 |
91 | server.tool(
92 | 'examples-tool',
93 | 'Fetches code examples and sample implementations from the documentation. Use this tool when users specifically request examples, sample code, or implementation demonstrations. This tool provides practical code snippets and complete working examples that demonstrate how to implement various features.',
94 | {
95 | url: z
96 | .string()
97 | .describe(
98 | 'URL of a specific examples page to fetch. Format: /examples/...'
99 | )
100 | },
101 | async ({ url }) => {
102 | const content = await fetchDocsPost(
103 | `https://langbase.com/docs${url}`
104 | );
105 | return {
106 | content: [
107 | {
108 | type: 'text',
109 | text: content
110 | }
111 | ]
112 | };
113 | }
114 | );
115 |
116 | server.tool(
117 | 'guide-tool',
118 | 'Fetches detailed guides and tutorials from the documentation. Use this tool when users explicitly request guides, tutorials, or how-to content. This tool provides step-by-step instructions and practical examples for implementing various features.',
119 | {
120 | url: z
121 | .string()
122 | .describe(
123 | 'URL of a specific guide page to fetch. Format: /guides/...'
124 | )
125 | },
126 | async ({ url }) => {
127 | const content = await fetchDocsPost(
128 | `https://langbase.com/docs${url}`
129 | );
130 | return {
131 | content: [
132 | {
133 | type: 'text',
134 | text: content
135 | }
136 | ]
137 | };
138 | }
139 | );
140 |
141 | server.tool(
142 | 'api-reference-tool',
143 | 'Fetches API reference documentation. Use this tool ONLY when the user explicitly asks about API endpoints, REST API calls, or programmatically creating/updating/deleting resources (like pipes, memory, etc.) through the API interface. For general SDK implementation questions, use the sdk-documentation-fetcher instead.',
144 | {
145 | url: z
146 | .string()
147 | .describe(
148 | 'URL of a specific api-reference page to fetch. Format: /api-reference/...'
149 | )
150 | },
151 | async ({ url }) => {
152 | const content = await fetchDocsPost(
153 | `https://langbase.com/docs${url}`
154 | );
155 | return {
156 | content: [
157 | {
158 | type: 'text',
159 | text: content
160 | }
161 | ]
162 | };
163 | }
164 | );
165 |
166 | async function main() {
167 | const transport = new StdioServerTransport();
168 |
169 | try {
170 | await server.connect(transport);
171 | } catch (error) {
172 | console.error('Error connecting to transport:', error);
173 | process.exit(1);
174 | }
175 | }
176 |
177 | main().catch(error => {
178 | console.error('Something went wrong:', error);
179 | process.exit(1);
180 | });
181 | }
182 |
--------------------------------------------------------------------------------
/packages/cli/src/index.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import { auth } from './auth';
3 | import { build } from './build';
4 | import { deploy } from './deploy';
5 | import { docsMcpServer } from './docs-mcp-server';
6 | import cli from './utils/cli';
7 | import debugMode from './utils/debug-mode';
8 | import cliInit from './utils/init';
9 |
10 | const { flags, input, showHelp } = cli;
11 | const { clear, debug } = flags;
12 |
13 | // Utility function to check if a command is present
14 | const command = (cmd: string): boolean => input.includes(cmd);
15 |
16 | // Utility function to check if a flag is present
17 | const flag = (flg: string): boolean => Boolean(flags[flg]);
18 |
19 | (async () => {
20 | // Skip welcome message for docs-mcp-server command
21 | if (!command('docs-mcp-server')) {
22 | await cliInit({ clear });
23 | }
24 | if (debug) debugMode(cli);
25 |
26 | if (command('help')) {
27 | showHelp(0);
28 | }
29 |
30 | if (command('auth')) {
31 | await auth();
32 | }
33 |
34 | if (command('build')) {
35 | const filePath = flags.file;
36 | await build({ filePath });
37 |
38 | // await deploy({ isDev, agent, filePath, apiKey });
39 | }
40 |
41 | if (command('deploy')) {
42 | const isDev = flag('dev');
43 | const agent = flags.agent;
44 | const filePath = flags.file;
45 | const apiKey = flags.key;
46 |
47 | await deploy({ isDev, agent, filePath, apiKey });
48 | }
49 |
50 | if (command('docs-mcp-server')) {
51 | await docsMcpServer();
52 | }
53 | })();
54 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/build.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs/promises';
2 | import path from 'path';
3 | import esbuild from 'esbuild';
4 | import { getWorkerTemplaleCode } from '@/data';
5 | import { Spinner } from 'types';
6 | import * as p from '@clack/prompts';
7 |
8 | export async function buildCode({
9 | spinner,
10 | filePath
11 | }: {
12 | filePath: string;
13 | spinner: Spinner;
14 | }) {
15 | spinner.start('Building code...');
16 | const cwd = process.cwd();
17 | const srcPath = path.join(cwd, filePath);
18 | const distPath = path.join(cwd, 'dist');
19 | const isTypescript = filePath.endsWith('.ts');
20 | const workerTemplateFileName = isTypescript ? 'langbase.ts' : 'langbase.js';
21 |
22 | try {
23 | await fs.access(distPath);
24 | } catch (error) {
25 | // Create the build directory if it doesn't exist
26 | await fs.mkdir(distPath, { recursive: true });
27 | }
28 |
29 | try {
30 | const code = getWorkerTemplaleCode(srcPath);
31 | const workerTemplatePath = path.join(distPath, workerTemplateFileName);
32 |
33 | await fs.writeFile(workerTemplatePath, code);
34 |
35 | await esbuild.build({
36 | entryPoints: [workerTemplatePath],
37 | bundle: true, // Bundle all dependencies into a single file
38 | outfile: 'dist/langbase.build.js', // Output file
39 | platform: 'browser', // Target platform (browser-like environment)
40 | target: 'es2020', // Target ES version (Cloudflare Workers supports modern JS)
41 | format: 'esm', // Use ES modules format (recommended for CF Workers)
42 | minify: true, // Minify the output (optional)
43 | sourcemap: false // Generate source maps (optional)
44 | });
45 |
46 | spinner.stop('Code built successfully');
47 |
48 | return { distPath };
49 | } catch (error) {
50 | spinner.stop('Failed to build code');
51 | p.cancel(`Error building code: ${error}`);
52 | process.exit(1);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/cli.ts:
--------------------------------------------------------------------------------
1 | import meowHelp from 'cli-meow-help';
2 | // @ts-ignore
3 | import meow from 'meow';
4 |
5 | const flags = {
6 | clear: {
7 | type: `boolean`,
8 | default: false,
9 | desc: `Clear the console`
10 | },
11 | debug: {
12 | type: `boolean`,
13 | default: false,
14 | shortFlag: `d`,
15 | desc: `Print debug info`
16 | }
17 | // agent: {
18 | // type: `string`,
19 | // shortFlag: `a`,
20 | // desc: `Agent to deploy the script into`
21 | // },
22 | // file: {
23 | // type: `string`,
24 | // shortFlag: `f`,
25 | // desc: `Script path to deploy`
26 | // },
27 | // key: {
28 | // type: `string`,
29 | // shortFlag: `k`,
30 | // desc: `Langbase API key`
31 | // }
32 | };
33 |
34 | const commands = {
35 | 'docs-mcp-server': {
36 | desc: `Start the Langbase docs MCP server`
37 | },
38 | // auth: { desc: `Authenticate with Langbase` },
39 | // deploy: { desc: `Deploy a script to Langbase` },
40 | help: { desc: `Print help info` }
41 | };
42 |
43 | const helpText = meowHelp({
44 | name: `@langbase/cli`,
45 | flags,
46 | commands,
47 | desc: false,
48 | header: false,
49 | footer: `Made by Langbase. For more https://langbase.com/docs`
50 | });
51 |
52 | const options = {
53 | importMeta: import.meta,
54 | inferType: true,
55 | description: false,
56 | hardRejection: false,
57 | flags
58 | };
59 |
60 | export default meow(helpText, options);
61 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/common/errors.ts:
--------------------------------------------------------------------------------
1 | import { Headers } from './../../../types'; // Ensure this import is correct
2 |
3 | export class APIError extends Error {
4 | readonly status: number | undefined;
5 | readonly headers: Headers | undefined;
6 | readonly error: Object | undefined;
7 |
8 | readonly code: string | null | undefined;
9 | readonly param: string | null | undefined;
10 | readonly type: string | undefined;
11 |
12 | readonly request_id: string | null | undefined;
13 |
14 | constructor(
15 | status: number | undefined,
16 | error: Object | undefined,
17 | message: string | undefined,
18 | headers: Headers | undefined
19 | ) {
20 | super(APIError.makeMessage(status, error, message));
21 | this.status = status;
22 | this.headers = headers;
23 | this.request_id = headers?.['lb-request-id'];
24 |
25 | const data = error as Record;
26 | this.error = data;
27 | this.code = data?.['code'];
28 | this.status = data?.['status'];
29 | // this.param = data?.['param'];
30 | // this.type = data?.['type'];
31 | }
32 |
33 | private static makeMessage(
34 | status: number | undefined,
35 | error: any,
36 | message: string | undefined
37 | ): string {
38 | const msg = error?.message
39 | ? typeof error.message === 'string'
40 | ? error.message
41 | : JSON.stringify(error.message)
42 | : error
43 | ? JSON.stringify(error)
44 | : message;
45 |
46 | if (status && msg) {
47 | return `${status} ${msg}`;
48 | }
49 | if (status) {
50 | return `${status} status code (no body)`;
51 | }
52 | if (msg) {
53 | return msg;
54 | }
55 | return '(no status code or body)';
56 | }
57 |
58 | static generate(
59 | status: number | undefined,
60 | errorResponse: Object | undefined,
61 | message: string | undefined,
62 | headers: Headers | undefined
63 | ): APIError {
64 | if (!status) {
65 | return new APIConnectionError({
66 | cause:
67 | errorResponse instanceof Error ? errorResponse : undefined
68 | });
69 | }
70 |
71 | const error = (errorResponse as Record)?.['error'];
72 |
73 | switch (status) {
74 | case 400:
75 | return new BadRequestError(status, error, message, headers);
76 | case 401:
77 | return new AuthenticationError(status, error, message, headers);
78 | case 403:
79 | return new PermissionDeniedError(
80 | status,
81 | error,
82 | message,
83 | headers
84 | );
85 | case 404:
86 | return new NotFoundError(status, error, message, headers);
87 | case 409:
88 | return new ConflictError(status, error, message, headers);
89 | case 422:
90 | return new UnprocessableEntityError(
91 | status,
92 | error,
93 | message,
94 | headers
95 | );
96 | case 429:
97 | return new RateLimitError(status, error, message, headers);
98 | default:
99 | return status >= 500
100 | ? new InternalServerError(status, error, message, headers)
101 | : new APIError(status, error, message, headers);
102 | }
103 | }
104 | }
105 |
106 | export class APIConnectionError extends APIError {
107 | override readonly status: undefined = undefined;
108 |
109 | constructor({ message, cause }: { message?: string; cause?: Error }) {
110 | super(undefined, undefined, message || 'Connection error.', undefined);
111 | if (cause) (this as Error).cause = cause;
112 | }
113 | }
114 |
115 | export class APIConnectionTimeoutError extends APIConnectionError {
116 | constructor({ message }: { message?: string } = {}) {
117 | super({ message: message ?? 'Request timed out.' });
118 | }
119 | }
120 |
121 | export class BadRequestError extends APIError {
122 | override readonly status: 400 = 400;
123 | }
124 |
125 | export class AuthenticationError extends APIError {
126 | override readonly status: 401 = 401;
127 | }
128 |
129 | export class PermissionDeniedError extends APIError {
130 | override readonly status: 403 = 403;
131 | }
132 |
133 | export class NotFoundError extends APIError {
134 | override readonly status: 404 = 404;
135 | }
136 |
137 | export class ConflictError extends APIError {
138 | override readonly status: 409 = 409;
139 | }
140 |
141 | export class UnprocessableEntityError extends APIError {
142 | override readonly status: 422 = 422;
143 | }
144 |
145 | export class RateLimitError extends APIError {
146 | override readonly status: 429 = 429;
147 | }
148 |
149 | export class InternalServerError extends APIError {}
150 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/common/request.ts:
--------------------------------------------------------------------------------
1 | import { type Headers } from './../../../types'; // Ensure this import is correct
2 | import { APIConnectionError, APIError } from './errors';
3 | import { Stream } from './stream';
4 |
5 | interface RequestOptions {
6 | endpoint: string;
7 | method: string;
8 | headers?: Record;
9 | body?: any;
10 | stream?: boolean;
11 | }
12 |
13 | interface RequestConfig {
14 | apiKey: string;
15 | baseUrl: string;
16 | timeout?: number;
17 | }
18 |
19 | interface SendOptions extends RequestOptions {
20 | endpoint: string;
21 | }
22 |
23 | interface MakeRequestParams {
24 | url: string;
25 | options: RequestOptions;
26 | headers: Record;
27 | }
28 |
29 | interface HandleGenerateResponseParams {
30 | response: Response;
31 | isChat: boolean;
32 | threadId: string | null;
33 | }
34 |
35 | export class Request {
36 | private config: RequestConfig;
37 |
38 | constructor(config: RequestConfig) {
39 | this.config = config;
40 | }
41 |
42 | // Main send function
43 | private async send({ endpoint, ...options }: SendOptions): Promise {
44 | const url = this.buildUrl({ endpoint });
45 | const headers = this.buildHeaders({ headers: options.headers });
46 |
47 | let response: Response;
48 | try {
49 | response = await this.makeRequest({
50 | url,
51 | headers,
52 | options: { ...options, endpoint }
53 | });
54 | } catch (error) {
55 | throw new APIConnectionError({
56 | cause: error instanceof Error ? error : undefined
57 | });
58 | }
59 |
60 | if (!response.ok) {
61 | await this.handleErrorResponse({ response });
62 | }
63 |
64 | if (!options.body) {
65 | return this.handleGenerateResponse({
66 | response,
67 | isChat: false,
68 | threadId: null
69 | });
70 | }
71 |
72 | const threadId = response.headers.get('lb-thread-id');
73 |
74 | if (options.body?.stream && url.includes('run')) {
75 | return this.handleRunResponseStream({
76 | response,
77 | rawResponse: options.body.rawResponse
78 | }) as T;
79 | }
80 |
81 | if (options.body.stream) {
82 | return this.handleStreamResponse({ response }) as T;
83 | }
84 |
85 | return this.handleGenerateResponse({
86 | response,
87 | isChat: options.body.chat,
88 | threadId
89 | });
90 | }
91 |
92 | private buildUrl({ endpoint }: { endpoint: string }): string {
93 | return `${this.config.baseUrl}${endpoint}`;
94 | }
95 |
96 | private buildHeaders({
97 | headers
98 | }: {
99 | headers?: Record;
100 | }): Record {
101 | return {
102 | 'Content-Type': 'application/json',
103 | Authorization: `Bearer ${this.config.apiKey}`,
104 | ...headers
105 | };
106 | }
107 |
108 | private async makeRequest({
109 | url,
110 | options,
111 | headers
112 | }: MakeRequestParams): Promise {
113 | return fetch(url, {
114 | method: options.method,
115 | headers,
116 | body: JSON.stringify(options.body)
117 | // signal: AbortSignal.timeout(this.config.timeout || 30000),
118 | });
119 | }
120 |
121 | private async handleErrorResponse({
122 | response
123 | }: {
124 | response: Response;
125 | }): Promise {
126 | let errorBody;
127 | try {
128 | errorBody = await response.json();
129 | } catch {
130 | errorBody = await response.text();
131 | }
132 | throw APIError.generate(
133 | response.status,
134 | errorBody,
135 | response.statusText,
136 | response.headers as unknown as Headers
137 | );
138 | }
139 |
140 | private handleStreamResponse({ response }: { response: Response }): {
141 | stream: any;
142 | threadId: string | null;
143 | } {
144 | const controller = new AbortController();
145 | const stream = Stream.fromSSEResponse(response, controller);
146 | return { stream, threadId: response.headers.get('lb-thread-id') };
147 | }
148 |
149 | private handleRunResponseStream({
150 | response,
151 | rawResponse
152 | }: {
153 | response: Response;
154 | rawResponse?: boolean;
155 | }): {
156 | stream: any;
157 | threadId: string | null;
158 | rawResponse?: {
159 | headers: Record;
160 | };
161 | } {
162 | const controller = new AbortController();
163 | const streamSSE = Stream.fromSSEResponse(response, controller);
164 | const stream = streamSSE.toReadableStream();
165 |
166 | const result: {
167 | stream: ReadableStream;
168 | threadId: string | null;
169 | rawResponse?: {
170 | headers: Record;
171 | };
172 | } = {
173 | stream,
174 | threadId: response.headers.get('lb-thread-id')
175 | };
176 | if (rawResponse) {
177 | result.rawResponse = {
178 | headers: Object.fromEntries(response.headers.entries())
179 | };
180 | }
181 | return result;
182 | }
183 |
184 | private async handleGenerateResponse({
185 | response,
186 | isChat,
187 | threadId
188 | }: HandleGenerateResponseParams): Promise {
189 | const generateResponse = await response.json();
190 | const buildResponse = generateResponse.raw
191 | ? {
192 | completion: generateResponse.completion,
193 | ...generateResponse.raw
194 | }
195 | : generateResponse;
196 |
197 | // Chat.
198 | if (isChat && threadId) {
199 | return {
200 | threadId,
201 | ...buildResponse
202 | };
203 | }
204 |
205 | // Generate.
206 | return buildResponse;
207 | }
208 |
209 | async post(options: Omit): Promise {
210 | return this.send({ ...options, method: 'POST' });
211 | }
212 |
213 | async get(options: Omit): Promise {
214 | return this.send({ ...options, method: 'GET' });
215 | }
216 |
217 | async put(options: Omit): Promise {
218 | return this.send({ ...options, method: 'PUT' });
219 | }
220 |
221 | async delete(
222 | options: Omit
223 | ): Promise {
224 | return this.send({ ...options, method: 'DELETE' });
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/debug-mode.ts:
--------------------------------------------------------------------------------
1 | import * as p from '@clack/prompts';
2 | import { heading } from './heading';
3 |
4 | export default function debugMode(cli: any) {
5 | p.intro(
6 | heading({
7 | text: 'DEBUG MODE',
8 | sub: 'logs will be verbose...',
9 | dim: true
10 | })
11 | );
12 | console.log();
13 |
14 | p.intro(
15 | heading({
16 | text: 'cwd',
17 | dim: true
18 | })
19 | );
20 | console.log(process.cwd());
21 | console.log();
22 |
23 | p.intro(
24 | heading({
25 | text: 'cli.flags',
26 | dim: true
27 | })
28 | );
29 | console.log(cli.flags);
30 | console.log();
31 |
32 | p.intro(
33 | heading({
34 | text: 'cli.input',
35 | sub: 'commands',
36 | dim: true
37 | })
38 | );
39 | console.log(cli.input);
40 | console.log();
41 | }
42 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/find-env.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import fs from 'fs/promises';
3 |
4 | export async function findEnvFile() {
5 | const envFiles = ['.env', '.env.local', '.dev.vars'];
6 |
7 | for (const file of envFiles) {
8 | try {
9 | const filePath = path.join(process.cwd(), file);
10 | await fs.access(filePath);
11 | return file; // Return the first file that exists
12 | } catch (error) {
13 | // File doesn't exist, continue to next file
14 | continue;
15 | }
16 | }
17 |
18 | return null; // Return null if no env file is found
19 | }
20 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/formatting.ts:
--------------------------------------------------------------------------------
1 | import figures from 'figures';
2 | import pc from 'picocolors';
3 |
4 | export const color = {
5 | info: pc.cyan,
6 | success: pc.green,
7 | warning: pc.yellow,
8 | error: pc.red,
9 | green: pc.green,
10 | red: pc.red,
11 | yellow: pc.yellow,
12 | cyan: pc.cyan,
13 | blue: pc.blue,
14 | magenta: pc.magenta,
15 | white: pc.white,
16 | gray: pc.gray,
17 | dim: pc.dim,
18 | bold: pc.bold
19 | };
20 |
21 | export const dimItalic = (text: string) => pc.italic(pc.dim(text));
22 | export const dim = pc.dim;
23 | export const bold = pc.bold;
24 | export const underline = pc.underline;
25 | export const italic = pc.italic;
26 | export const inverse = pc.inverse;
27 | export const hidden = pc.hidden;
28 | export const strikethrough = pc.strikethrough;
29 | export const reset = pc.reset;
30 | export const black = pc.black;
31 | export const red = pc.red;
32 | export const green = pc.green;
33 | export const yellow = pc.yellow;
34 | export const blue = pc.blue;
35 | export const magenta = pc.magenta;
36 | export const cyan = pc.cyan;
37 | export const white = pc.white;
38 | export const gray = pc.gray;
39 | export const oneSpaced = ` `;
40 | export const twoSpaced = ` `;
41 | export const threeSpaced = ` `;
42 | export const fourSpaced = ` `;
43 | export const newLine = `\n`;
44 |
45 | export const pad = (n: number): string => {
46 | return ' '.repeat(n);
47 | };
48 |
49 | export const lineItem = pc.dim(`${figures.lineUpDownRight} `);
50 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/get-base-url.ts:
--------------------------------------------------------------------------------
1 | export function getBaseUrl(isDev: boolean) {
2 | return isDev ? 'http://localhost:8787' : 'https://api.langbase.com';
3 | }
4 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/get-score.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Calculates a relevance score between a line of text and a search query.
3 | *
4 | * The scoring algorithm works as follows:
5 | * - If the entire search query is found within the line, returns a score of 3 (highest relevance)
6 | * - Otherwise, adds 1 point for each individual search query word found in the line
7 | *
8 | * @param line - The text line to check against the search query
9 | * @param searchQuery - The search query to check against the line
10 | * @returns A numerical score indicating relevance: 3 for exact matches, or the count of matching words
11 | */
12 | export const getRelevanceScore = (line: string, searchQuery: string) => {
13 | const lowerLine = line.toLowerCase();
14 | const lowerQuery = searchQuery.toLowerCase();
15 | // Higher score for exact matches
16 | if (lowerLine.includes(lowerQuery)) {
17 | return 3;
18 | }
19 |
20 | // Score based on word matches
21 | const queryWords = lowerQuery.split(' ');
22 | return queryWords.reduce((score, word) => {
23 | return score + (lowerLine.includes(word) ? 1 : 0);
24 | }, 0);
25 | };
26 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/handle-error.ts:
--------------------------------------------------------------------------------
1 | import * as p from '@clack/prompts';
2 |
3 | type Spinner = ReturnType;
4 |
5 | export function handleError({
6 | spinner,
7 | message,
8 | error
9 | }: {
10 | spinner: Spinner;
11 | message: string;
12 | error: unknown;
13 | }): void {
14 | spinner.stop(message);
15 | p.log.error(`${message}: ${(error as Error).message}`);
16 | process.exit(1);
17 | }
18 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/heading.ts:
--------------------------------------------------------------------------------
1 | import color from 'picocolors';
2 |
3 | export function heading({
4 | text,
5 | sub,
6 | dim,
7 | green
8 | }: {
9 | text: string;
10 | sub?: string;
11 | dim?: boolean;
12 | green?: boolean;
13 | }) {
14 | if (green) {
15 | return `${color.bgGreen(color.black(` ${text} `))} ${sub && sub}`;
16 | }
17 | if (dim) {
18 | return `${color.bgBlack(color.white(` ${text} `))} ${sub && sub}`;
19 | }
20 | return `${color.bold(color.bgCyan(color.black(` ${text} `)))} ${sub && sub}`;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/init.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import unhandled from 'cli-handle-unhandled';
3 | import welcome from 'cli-welcome';
4 | import path from 'path';
5 | import { fileURLToPath } from 'url';
6 | import fs from 'fs';
7 |
8 | export default async ({ clear = false }) => {
9 | unhandled();
10 |
11 | // Get the directory of the current module
12 | const __filename = fileURLToPath(import.meta.url);
13 | const __dirname = path.dirname(__filename);
14 |
15 | // Go up two levels to find the package.json
16 | const pkgJsonPath = path.join(__dirname, '..', 'package.json');
17 |
18 | if (!fs.existsSync(pkgJsonPath)) {
19 | console.error('Unable to find package.json');
20 | process.exit(1);
21 | }
22 |
23 | const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
24 |
25 | await welcome({
26 | title: `cli`,
27 | tagLine: `by Langbase`,
28 | description: pkgJson.description,
29 | version: pkgJson.version,
30 | bgColor: '#A699EA',
31 | color: '#000000',
32 | bold: true,
33 | clear
34 | });
35 | };
36 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/retrieve-credentials.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs/promises';
2 | import * as p from '@clack/prompts';
3 | import color from 'picocolors';
4 | import { findEnvFile } from './find-env';
5 | import { cyan } from './formatting';
6 |
7 | export interface Account {
8 | apiKey: string;
9 | }
10 |
11 | type Spinner = ReturnType;
12 |
13 | function handleNoAccountFound({ spinner }: { spinner: Spinner }): void {
14 | spinner.stop('No account found');
15 | p.outro(
16 | `Skipping deployment. \n Run: ${cyan('npx lb auth')} to authenticate.`
17 | );
18 | }
19 |
20 | export async function retrieveAuthentication({
21 | spinner
22 | }: {
23 | spinner: Spinner;
24 | }): Promise {
25 | spinner.start('Retrieving stored authentication');
26 | try {
27 | let envFile = await findEnvFile();
28 | if (!envFile) {
29 | handleNoAccountFound({ spinner });
30 | process.exit(1);
31 | }
32 |
33 | envFile = `${process.cwd()}/${envFile}`;
34 |
35 | const envFileContent = await fs.readFile(envFile, 'utf-8');
36 |
37 | const apiKey = envFileContent
38 | .split('\n')
39 | .reverse()
40 | .find(line => line.includes('LANGBASE_API_KEY='))
41 | ?.split('=')[1];
42 |
43 | if (!apiKey) {
44 | handleNoAccountFound({ spinner });
45 | process.exit(1);
46 | }
47 |
48 | spinner.stop('Retrieved stored authentication');
49 |
50 | return apiKey;
51 | } catch (error) {
52 | spinner.stop('Failed to retrieve authentication');
53 | p.log.error(
54 | `Error retrieving stored auth: ${(error as Error).message}`
55 | );
56 | p.outro(
57 | `Skipping deployment. \n Run: ${cyan('npx lb auth')} to authenticate.`
58 | );
59 | process.exit(1);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/packages/cli/src/utils/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const deployOptionsSchema = z.object({
4 | isDev: z.boolean().optional().default(false),
5 | agent: z
6 | .string()
7 | .trim()
8 | .min(1)
9 | .refine(
10 | value => {
11 | const regex =
12 | /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\/[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/i;
13 | if (regex.test(value)) return true;
14 | else return false;
15 | },
16 | {
17 | message:
18 | 'Invalid agent name. Ensure the agent name is in the format: owner/agent'
19 | }
20 | ),
21 | filePath: z
22 | .string()
23 | .trim()
24 | .min(1)
25 | .refine(value => value.endsWith('.js') || value.endsWith('.ts'), {
26 | message: 'Only JavaScript and TypeScript files are supported'
27 | }),
28 | apiKey: z.string().min(1).optional()
29 | });
30 |
31 | export const buildOptionsSchema = z.object({
32 | filePath: z
33 | .string()
34 | .trim()
35 | .min(1)
36 | .refine(value => value.endsWith('.js') || value.endsWith('.ts'), {
37 | message: 'Only JavaScript and TypeScript files are supported'
38 | })
39 | });
40 |
--------------------------------------------------------------------------------
/packages/cli/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./node_modules/@langbase/tsconfig/react-library.json",
3 | "include": ["."],
4 | "exclude": ["*/dist", "dist", "build", "node_modules"],
5 | "compilerOptions": {
6 | "target": "ES2018",
7 | "stripInternal": true,
8 | "lib": ["dom", "dom.iterable", "esnext"],
9 | "types": ["@types/node", "vitest/globals"],
10 | "baseUrl": "." /* Specify the base directory to resolve non-relative module names. */,
11 | "paths": {
12 | "@/*": ["src/*"] // Maps all imports starting with "@" to the src folder
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/cli/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup';
2 |
3 | export default defineConfig({
4 | clean: true,
5 | dts: true,
6 | entry: ['src/index.ts'],
7 | format: ['esm'],
8 | sourcemap: true,
9 | // target: 'esnext',
10 | target: 'node16',
11 | outDir: 'dist',
12 | splitting: false,
13 | bundle: true,
14 | minify: true,
15 | external: ['react', 'svelte', 'vue']
16 | });
17 |
--------------------------------------------------------------------------------
/packages/cli/types/index.ts:
--------------------------------------------------------------------------------
1 | import * as p from '@clack/prompts';
2 |
3 | export type Headers = Record;
4 | export type Spinner = ReturnType;
5 |
--------------------------------------------------------------------------------
/packages/langbase/.env.example:
--------------------------------------------------------------------------------
1 | # Pipes.
2 | PIPE_LESS_WORDY=
3 |
--------------------------------------------------------------------------------
/packages/langbase/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: [
4 | '@langbase/eslint-config/library.js',
5 | 'plugin:prettier/recommended', // prettier
6 | ],
7 | parser: '@typescript-eslint/parser',
8 | parserOptions: {
9 | project: true,
10 | },
11 | plugins: ['prettier'], // prettier
12 | rules: {
13 | 'prettier/prettier': 'error', // prettier
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/packages/langbase/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | /test-results/
3 | /playwright-report/
4 | /blob-report/
5 | /playwright/.cache/
6 |
--------------------------------------------------------------------------------
/packages/langbase/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "langbase",
3 | "version": "1.1.59",
4 | "license": "Apache-2.0",
5 | "sideEffects": false,
6 | "main": "./dist/index.js",
7 | "module": "./dist/index.mjs",
8 | "types": "./dist/index.d.ts",
9 | "files": [
10 | "dist/**",
11 | "react/dist/**"
12 | ],
13 | "scripts": {
14 | "build": "tsup --config tsup.config.ts --format esm,cjs --dts --external react",
15 | "dev": "tsup --config tsup.config.ts --format esm,cjs --watch --dts --external react",
16 | "xdev": "tsup --watch",
17 | "lint": "eslint \"src/**/*.ts*\"",
18 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
19 | "type-check": "tsc --noEmit",
20 | "prettier-check": "prettier --check \"./**/*.ts*\"",
21 | "test": "pnpm test:node && pnpm test:edge",
22 | "#test": "pnpm test:node && pnpm test:edge && pnpm test:ui && pnpm test:e2e",
23 | "test:edge": "vitest --config vitest.edge.config.js --run",
24 | "test:node": "vitest --config vitest.node.config.js --run",
25 | "test:ui": "pnpm test:ui:react",
26 | "test:ui:react": "vitest --config vitest.ui.react.config.js --run",
27 | "test:e2e": "playwright test",
28 | "test:edge:watch": "vitest --config vitest.edge.config.js",
29 | "test:node:watch": "vitest --config vitest.node.config.js",
30 | "test:ui:react:watch": "vitest --config vitest.ui.react.config.js"
31 | },
32 | "dependencies": {
33 | "dotenv": "^16.4.5",
34 | "openai": "^4.82.0",
35 | "zod": "^3.23.8",
36 | "zod-validation-error": "^3.3.0"
37 | },
38 | "devDependencies": {
39 | "@edge-runtime/vm": "^4.0.1",
40 | "@langbase/eslint-config": "workspace:*",
41 | "@langbase/tsconfig": "workspace:*",
42 | "@playwright/test": "^1.44.1",
43 | "@testing-library/react": "^16.0.1",
44 | "@types/node": "^20.11.24",
45 | "@types/react": "^18.3.9",
46 | "@vitejs/plugin-react": "^4.3.1",
47 | "eslint": "^8.57.0",
48 | "jsdom": "^24.1.0",
49 | "react": "^18",
50 | "tsup": "^8.3.6",
51 | "typescript": "^5.3.3",
52 | "vitest": "1.6.0"
53 | },
54 | "publishConfig": {
55 | "access": "public"
56 | },
57 | "exports": {
58 | "./package.json": "./package.json",
59 | ".": {
60 | "types": "./dist/index.d.ts",
61 | "import": "./dist/index.mjs",
62 | "require": "./dist/index.js"
63 | },
64 | "./react": {
65 | "types": "./react/dist/index.d.ts",
66 | "require": "./react/dist/index.js",
67 | "import": "./react/dist/index.mjs"
68 | }
69 | },
70 | "engines": {
71 | "node": ">=18"
72 | },
73 | "homepage": "https://langbase.com/docs",
74 | "repository": {
75 | "type": "git",
76 | "url": "git+https://github.com/LangbaseInc/langbase-sdk.git"
77 | },
78 | "bugs": {
79 | "url": "https://github.com/LangbaseInc/langbase-sdk/issues"
80 | },
81 | "keywords": [
82 | "ai",
83 | "llm",
84 | "langbase core",
85 | "langbase sdk",
86 | "baseai",
87 | "base ai",
88 | "langbase",
89 | "langbase.com",
90 | "generative AI"
91 | ],
92 | "peerDependencies": {
93 | "react": "^18 || ^19"
94 | },
95 | "peerDependenciesMeta": {
96 | "react": {
97 | "optional": true
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/packages/langbase/playwright.config.ts:
--------------------------------------------------------------------------------
1 | // Importing the PlaywrightTestConfig type from the Playwright test library
2 | import type {PlaywrightTestConfig} from '@playwright/test';
3 | // Importing devices configuration from the Playwright test library
4 | import {devices} from '@playwright/test';
5 |
6 | // Setting the PORT variable to the environment's PORT or defaulting to 3000
7 | const PORT = process.env.PORT || 3000;
8 | // Constructing the base URL using the PORT variable
9 | const baseURL = `http://localhost:${PORT}`;
10 |
11 | /**
12 | * Configuration object for Playwright tests.
13 | * For more details, refer to https://playwright.dev/docs/test-configuration.
14 | */
15 | const config: PlaywrightTestConfig = {
16 | // Directory where the end-to-end test specifications are located
17 | testDir: './tests/e2e/spec',
18 | // Template path for storing snapshots
19 | snapshotPathTemplate: './tests/e2e/__snapshots__/{testFilePath}/{arg}{ext}',
20 | // Global timeout for each test in milliseconds
21 | timeout: 20_000,
22 | // Configuration for expectations
23 | expect: {
24 | // Timeout for assertions in milliseconds
25 | timeout: 10_000,
26 | },
27 | // Indicates if tests should run in parallel
28 | fullyParallel: false,
29 | // Number of worker processes for running tests
30 | workers: 3,
31 | // Number of retry attempts for failed tests
32 | retries: 2,
33 | // Reporter configuration based on whether the environment is CI
34 | reporter: process.env.CI
35 | ? [['github'], ['json', {outputFile: 'test-results.json'}]]
36 | : 'list',
37 | // Projects configuration for different browsers/devices
38 | projects: [
39 | {
40 | // Name of the project
41 | name: 'chromium',
42 | // Using the Desktop Chrome configuration from devices
43 | use: devices['Desktop Chrome'],
44 | },
45 | ],
46 | // Shared context configuration for tests
47 | use: {
48 | // Base URL for the tests
49 | baseURL,
50 | // Trace configuration to retain traces on test failure
51 | trace: 'retain-on-failure',
52 | // Custom user agent for the tests
53 | userAgent: 'playwright-test bot',
54 | },
55 | // Configuration for the web server that runs during tests
56 | webServer: {
57 | // Current working directory for the server command
58 | cwd: './tests/e2e/next-server',
59 | // Command to start the development server
60 | command: 'pnpm run dev',
61 | // URL to check if the server is up
62 | url: baseURL,
63 | // Timeout for the server to start in milliseconds
64 | timeout: 120 * 1000,
65 | // Whether to reuse an existing server if available
66 | reuseExistingServer: false,
67 | },
68 | };
69 |
70 | // Exporting the configuration object as the default export
71 | export default config;
72 |
--------------------------------------------------------------------------------
/packages/langbase/readme.md:
--------------------------------------------------------------------------------
1 | # Langbase SDK
2 |
3 | The AI SDK for building declarative and composable AI-powered LLM products.
4 |
5 | ## Documentation
6 |
7 | > [!NOTE]
8 | > Check the [Langbase SDK documentation](https://langbase.com/docs/sdk) for more details.
9 |
10 | The following examples are for reference only. Prefer docs for the latest information.
11 |
12 | ## Getting started with `langbase` SDK
13 |
14 | ### Installation
15 |
16 | First, install the `langbase` package using npm or yarn:
17 |
18 | ```bash
19 | npm install langbase
20 | ```
21 |
22 | or
23 |
24 | ```bash
25 | pnpm add langbase
26 | ```
27 |
28 | or
29 |
30 | ```bash
31 | yarn add langbase
32 | ```
33 |
34 | ## Documentation
35 |
36 | Please read the [SDK documentation](https://langbase.com/docs/sdk) →
37 |
38 | ## Examples
39 |
40 | Check out [more examples in the docs](https://langbase.com/docs/langbase-sdk/examples) →
41 |
--------------------------------------------------------------------------------
/packages/langbase/src/common/errors.ts:
--------------------------------------------------------------------------------
1 | import {Headers} from './../../types'; // Ensure this import is correct
2 |
3 | export class APIError extends Error {
4 | readonly status: number | undefined;
5 | readonly headers: Headers | undefined;
6 | readonly error: Object | undefined;
7 |
8 | readonly code: string | null | undefined;
9 | readonly param: string | null | undefined;
10 | readonly type: string | undefined;
11 |
12 | readonly request_id: string | null | undefined;
13 |
14 | constructor(
15 | status: number | undefined,
16 | error: Object | undefined,
17 | message: string | undefined,
18 | headers: Headers | undefined,
19 | ) {
20 | super(APIError.makeMessage(status, error, message));
21 | this.status = status;
22 | this.headers = headers;
23 | this.request_id = headers?.['lb-request-id'];
24 |
25 | const data = error as Record;
26 | this.error = data;
27 | this.code = data?.['code'];
28 | this.status = data?.['status'];
29 | // this.param = data?.['param'];
30 | // this.type = data?.['type'];
31 | }
32 |
33 | private static makeMessage(
34 | status: number | undefined,
35 | error: any,
36 | message: string | undefined,
37 | ): string {
38 | const msg = error?.message
39 | ? typeof error.message === 'string'
40 | ? error.message
41 | : JSON.stringify(error.message)
42 | : error
43 | ? JSON.stringify(error)
44 | : message;
45 |
46 | if (status && msg) {
47 | return `${status} ${msg}`;
48 | }
49 | if (status) {
50 | return `${status} status code (no body)`;
51 | }
52 | if (msg) {
53 | return msg;
54 | }
55 | return '(no status code or body)';
56 | }
57 |
58 | static generate(
59 | status: number | undefined,
60 | errorResponse: Object | undefined,
61 | message: string | undefined,
62 | headers: Headers | undefined,
63 | ): APIError {
64 | if (!status) {
65 | return new APIConnectionError({
66 | cause:
67 | errorResponse instanceof Error ? errorResponse : undefined,
68 | });
69 | }
70 |
71 | const error = (errorResponse as Record)?.['error'];
72 |
73 | switch (status) {
74 | case 400:
75 | return new BadRequestError(status, error, message, headers);
76 | case 401:
77 | return new AuthenticationError(status, error, message, headers);
78 | case 403:
79 | return new PermissionDeniedError(
80 | status,
81 | error,
82 | message,
83 | headers,
84 | );
85 | case 404:
86 | return new NotFoundError(status, error, message, headers);
87 | case 409:
88 | return new ConflictError(status, error, message, headers);
89 | case 422:
90 | return new UnprocessableEntityError(
91 | status,
92 | error,
93 | message,
94 | headers,
95 | );
96 | case 429:
97 | return new RateLimitError(status, error, message, headers);
98 | default:
99 | return status >= 500
100 | ? new InternalServerError(status, error, message, headers)
101 | : new APIError(status, error, message, headers);
102 | }
103 | }
104 | }
105 |
106 | export class APIConnectionError extends APIError {
107 | override readonly status: undefined = undefined;
108 |
109 | constructor({message, cause}: {message?: string; cause?: Error}) {
110 | super(undefined, undefined, message || 'Connection error.', undefined);
111 | if (cause) (this as Error).cause = cause;
112 | }
113 | }
114 |
115 | export class APIConnectionTimeoutError extends APIConnectionError {
116 | constructor({message}: {message?: string} = {}) {
117 | super({message: message ?? 'Request timed out.'});
118 | }
119 | }
120 |
121 | export class BadRequestError extends APIError {
122 | override readonly status: 400 = 400;
123 | }
124 |
125 | export class AuthenticationError extends APIError {
126 | override readonly status: 401 = 401;
127 | }
128 |
129 | export class PermissionDeniedError extends APIError {
130 | override readonly status: 403 = 403;
131 | }
132 |
133 | export class NotFoundError extends APIError {
134 | override readonly status: 404 = 404;
135 | }
136 |
137 | export class ConflictError extends APIError {
138 | override readonly status: 409 = 409;
139 | }
140 |
141 | export class UnprocessableEntityError extends APIError {
142 | override readonly status: 422 = 422;
143 | }
144 |
145 | export class RateLimitError extends APIError {
146 | override readonly status: 429 = 429;
147 | }
148 |
149 | export class InternalServerError extends APIError {}
150 |
--------------------------------------------------------------------------------
/packages/langbase/src/data/constants.ts:
--------------------------------------------------------------------------------
1 | export const GENERATION_ENDPOINTS = [
2 | '/v1/pipes/run',
3 | '/beta/chat',
4 | '/beta/generate',
5 | '/v1/agent/run',
6 | ];
7 |
--------------------------------------------------------------------------------
/packages/langbase/src/examples/pipes/generate-text.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Pipe} from '../../pipes/pipes';
3 |
4 | const myGeneratePipe = async () => {
5 | console.log('\n============= GENERATE PIPE =============');
6 |
7 | // Initiate the Pipe.
8 | const myPipe = new Pipe({
9 | apiKey: process.env.LANGBASE_SDK_GENERATE_PIPE!,
10 | });
11 |
12 | // Generate the text by asking a question.
13 | let {completion} = await myPipe.generateText({
14 | messages: [{role: 'user', content: 'Who is an AI Engineer?'}],
15 | });
16 | console.log(completion);
17 | };
18 |
19 | const myChatPipe = async () => {
20 | console.log('\n============= CHAT PIPE =============');
21 |
22 | // Initiate the Pipe.
23 | const myPipe = new Pipe({
24 | apiKey: process.env.LANGBASE_SDK_CHAT_PIPE!,
25 | });
26 |
27 | // Message 1: Tell the AI about something.
28 | const userMsg1 = 'My company is ⌘ Langbase.';
29 | let {completion, threadId} = await myPipe.generateText({
30 | messages: [{role: 'user', content: userMsg1}],
31 | chat: true,
32 | });
33 | console.log(`User: `, userMsg1);
34 | console.log(`AI: `, completion);
35 |
36 | // Message 2: Ask the AI about what you told in previous message.
37 | const userMsg2 = 'What is the name of my company?';
38 | const {completion: completion2} = await myPipe.generateText({
39 | messages: [{role: 'user', content: userMsg2}],
40 | threadId,
41 | chat: true,
42 | });
43 | console.log(`User: `, userMsg2);
44 | console.log(`AI: `, completion2);
45 | };
46 |
47 | (async () => {
48 | await myGeneratePipe();
49 | await myChatPipe();
50 | })();
51 |
--------------------------------------------------------------------------------
/packages/langbase/src/examples/pipes/stream-text.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import {Pipe, printStreamToStdout, StreamText} from '../../pipes/pipes';
3 |
4 | // You can write your own — or use printStreamToStdout() from the SDK.
5 | const helperPrintStream = async (stream: StreamText) => {
6 | for await (const chunk of stream) {
7 | // Streaming text part — a single word or several.
8 | const textPart = chunk.choices[0]?.delta?.content || '';
9 |
10 | // Demo: Print the stream — you can use it however.
11 | process.stdout.write(textPart);
12 | }
13 | };
14 |
15 | const printChat = async ({
16 | userMessage,
17 | stream,
18 | }: {
19 | userMessage: string;
20 | stream: StreamText;
21 | }) => {
22 | console.log(`\n`);
23 | console.log(`User: `, userMessage);
24 | console.log(`AI: `);
25 | await printStreamToStdout(stream);
26 | };
27 |
28 | const myGeneratePipe = async () => {
29 | console.log('\n============= GENERATE PIPE =============');
30 |
31 | // Initiate the Pipe.
32 | const myPipe = new Pipe({
33 | apiKey: process.env.LANGBASE_SDK_GENERATE_PIPE!,
34 | });
35 |
36 | // Generate the text by asking a question.
37 | const userMsg = 'Who is an AI Engineer?';
38 | let {stream} = await myPipe.streamText({
39 | messages: [{role: 'user', content: userMsg}],
40 | });
41 |
42 | // Print.
43 | await printChat({userMessage: userMsg, stream});
44 | };
45 |
46 | const myChatPipe = async () => {
47 | console.log('\n\n============= CHAT PIPE =============');
48 |
49 | // Initiate the Pipe.
50 | const myPipe = new Pipe({
51 | apiKey: process.env.LANGBASE_SDK_CHAT_PIPE!,
52 | });
53 |
54 | // Message 1: Tell the AI about something.
55 | const userMsg1 = 'My company is ⌘ Langbase.';
56 | let {stream, threadId} = await myPipe.streamText({
57 | messages: [{role: 'user', content: userMsg1}],
58 | chat: true,
59 | });
60 |
61 | await printChat({userMessage: userMsg1, stream});
62 |
63 | // Message 2: Ask the AI about what you told in previous message.
64 | const userMsg2 = 'What is the name of my company?';
65 | const {stream: stream2} = await myPipe.streamText({
66 | messages: [{role: 'user', content: userMsg2}],
67 | threadId,
68 | chat: true,
69 | });
70 | await printChat({userMessage: userMsg2, stream: stream2});
71 | };
72 |
73 | (async () => {
74 | await myGeneratePipe();
75 | await myChatPipe();
76 | })();
77 |
--------------------------------------------------------------------------------
/packages/langbase/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './langbase/langbase';
2 | export * from './pipes/pipes';
3 | export * from './lib/helpers';
4 | export * from './langbase/workflows';
5 |
--------------------------------------------------------------------------------
/packages/langbase/src/langbase/workflows.ts:
--------------------------------------------------------------------------------
1 | type WorkflowContext = {
2 | outputs: Record;
3 | };
4 |
5 | type RetryConfig = {
6 | limit: number;
7 | delay: number;
8 | backoff: 'exponential' | 'linear' | 'fixed';
9 | };
10 |
11 | type StepConfig = {
12 | id: string;
13 | timeout?: number;
14 | retries?: RetryConfig;
15 | run: () => Promise;
16 | };
17 |
18 | class TimeoutError extends Error {
19 | constructor(stepId: string, timeout: number) {
20 | super(`Step "${stepId}" timed out after ${timeout}ms`);
21 | this.name = 'TimeoutError';
22 | }
23 | }
24 |
25 | export class Workflow {
26 | private context: WorkflowContext;
27 | private debug: boolean;
28 | public readonly step: (config: StepConfig) => Promise;
29 |
30 | constructor(config: {debug?: boolean} = {debug: false}) {
31 | this.context = {outputs: {}};
32 | this.debug = config.debug ?? false;
33 | this.step = this._step.bind(this);
34 | }
35 |
36 | private async _step(config: StepConfig): Promise {
37 | if (this.debug) {
38 | console.log(`\n🔄 Starting step: ${config.id}`);
39 | console.time(`⏱️ Step ${config.id}`);
40 | if (config.timeout) console.log(`⏳ Timeout: ${config.timeout}ms`);
41 | if (config.retries)
42 | console.log(`🔄 Retries: ${JSON.stringify(config.retries)}`);
43 | }
44 |
45 | let lastError: Error | null = null;
46 | let attempt = 1;
47 | const maxAttempts = config.retries?.limit
48 | ? config.retries.limit + 1
49 | : 1;
50 |
51 | while (attempt <= maxAttempts) {
52 | try {
53 | let stepPromise = config.run();
54 |
55 | if (config.timeout) {
56 | stepPromise = this.withTimeout({
57 | promise: stepPromise,
58 | timeout: config.timeout,
59 | stepId: config.id,
60 | });
61 | }
62 |
63 | const result = await stepPromise;
64 | this.context.outputs[config.id] = result;
65 |
66 | if (this.debug) {
67 | console.timeEnd(`⏱️ Step ${config.id}`);
68 | console.log(`📤 Output:`, result);
69 | console.log(`✅ Completed step: ${config.id}\n`);
70 | }
71 |
72 | return result;
73 | } catch (error) {
74 | lastError = error as Error;
75 |
76 | if (attempt < maxAttempts) {
77 | const delay = config.retries
78 | ? this.calculateDelay(
79 | config.retries.delay,
80 | attempt,
81 | config.retries.backoff,
82 | )
83 | : 0;
84 |
85 | if (this.debug) {
86 | console.log(
87 | `⚠️ Attempt ${attempt} failed, retrying in ${delay}ms...`,
88 | );
89 | console.error(error);
90 | }
91 |
92 | await this.sleep(delay);
93 | attempt++;
94 | } else {
95 | if (this.debug) {
96 | console.timeEnd(`⏱️ Step ${config.id}`);
97 | console.error(`❌ Failed step: ${config.id}`, error);
98 | }
99 | throw lastError;
100 | }
101 | }
102 | }
103 |
104 | throw lastError;
105 | }
106 |
107 | private async withTimeout({
108 | promise,
109 | timeout,
110 | stepId,
111 | }: {
112 | promise: Promise;
113 | timeout: number;
114 | stepId: string;
115 | }): Promise {
116 | const timeoutPromise = new Promise((_, reject) => {
117 | setTimeout(
118 | () => reject(new TimeoutError(stepId, timeout)),
119 | timeout,
120 | );
121 | });
122 | return Promise.race([promise, timeoutPromise]);
123 | }
124 |
125 | private calculateDelay(
126 | baseDelay: number,
127 | attempt: number,
128 | backoff: RetryConfig['backoff'],
129 | ): number {
130 | switch (backoff) {
131 | case 'exponential':
132 | return baseDelay * Math.pow(2, attempt - 1);
133 | case 'linear':
134 | return baseDelay * attempt;
135 | case 'fixed':
136 | default:
137 | return baseDelay;
138 | }
139 | }
140 |
141 | private async sleep(ms: number): Promise {
142 | return new Promise(resolve => setTimeout(resolve, ms));
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/packages/langbase/src/lib/helpers/index.ts:
--------------------------------------------------------------------------------
1 | import {ChatCompletionStream} from 'openai/lib/ChatCompletionStream';
2 | import {Stream} from 'openai/streaming';
3 | import {ChatCompletionMessageToolCall} from 'openai/resources/chat/completions';
4 | import {RunResponse, RunResponseStream} from '@/langbase/langbase';
5 |
6 | export interface Runner extends ChatCompletionStream {}
7 |
8 | export interface ToolCallResult extends ChatCompletionMessageToolCall {}
9 |
10 | export type MessageRole = 'function' | 'assistant' | 'system' | 'user' | 'tool';
11 |
12 | interface Delta {
13 | role?: MessageRole;
14 | content?: string;
15 | tool_calls?: ToolCallResult[];
16 | }
17 |
18 | interface ChoiceStream {
19 | index: number;
20 | delta: Delta;
21 | logprobs: boolean | null;
22 | finish_reason: string;
23 | }
24 |
25 | export interface ChunkStream {
26 | id: string;
27 | object: string;
28 | created: number;
29 | model: string;
30 | choices: ChoiceStream[];
31 | }
32 |
33 | /**
34 | * Converts a ReadableStream into a Runner.
35 | *
36 | * @param readableStream - The ReadableStream to convert.
37 | * @returns The converted Runner.
38 | */
39 | export const fromReadableStream = (readableStream: ReadableStream): Runner => {
40 | return ChatCompletionStream.fromReadableStream(readableStream);
41 | };
42 |
43 | /**
44 | * Returns a runner for the given readable stream.
45 | *
46 | * @param readableStream - The readable stream to create a runner for.
47 | * @returns A runner for the given readable stream.
48 | */
49 | export const getRunner = (readableStream: ReadableStream) => {
50 | return fromReadableStream(readableStream);
51 | };
52 |
53 | /**
54 | * Retrieves the text part from a given ChunkStream.
55 | *
56 | * @param chunk - The ChunkStream object.
57 | * @returns The text content of the first choice's delta, or an empty string if it doesn't exist.
58 | */
59 | export const getTextPart = (chunk: ChunkStream) => {
60 | return chunk.choices[0]?.delta?.content || '';
61 | };
62 |
63 | /**
64 | * Handles the response stream from a given `Response` object.
65 | *
66 | * @param {Object} params - The parameters for handling the response stream.
67 | * @param {Response} params.response - The API response to handle.
68 | * @param {boolean} params.rawResponse - Optional flag to include raw response headers.
69 | *
70 | * @returns {Object} An object containing the processed stream, thread ID, and optionally raw response headers.
71 | * @returns {ReadableStream} return.stream - The readable stream created from the response.
72 | * @returns {string | null} return.threadId - The thread ID extracted from the response headers.
73 | * @returns {Object} [return.rawResponse] - Optional raw response headers.
74 | * @returns {Record} return.rawResponse.headers - The headers from the raw response.
75 | */
76 | export function handleResponseStream({
77 | response,
78 | rawResponse,
79 | }: {
80 | response: Response;
81 | rawResponse?: boolean;
82 | }): {
83 | stream: any;
84 | threadId: string | null;
85 | rawResponse?: {
86 | headers: Record;
87 | };
88 | } {
89 | const controller = new AbortController();
90 | const streamSSE = Stream.fromSSEResponse(response, controller);
91 | const stream = streamSSE.toReadableStream();
92 |
93 | const result: {
94 | stream: ReadableStream;
95 | threadId: string | null;
96 | rawResponse?: {
97 | headers: Record;
98 | };
99 | } = {
100 | stream,
101 | threadId: response.headers.get('lb-thread-id'),
102 | };
103 | if (rawResponse) {
104 | result.rawResponse = {
105 | headers: Object.fromEntries(response.headers.entries()),
106 | };
107 | }
108 | return result;
109 | }
110 |
111 | /**
112 | * Retrieves tool calls from a given readable stream.
113 | *
114 | * @param stream - The readable stream from which to extract tool calls.
115 | * @returns A promise that resolves to an array of `ToolCall` objects.
116 | */
117 | export async function getToolsFromStream(
118 | stream: ReadableStream,
119 | ): Promise {
120 | let run = getRunner(stream);
121 | const {choices} = await run.finalChatCompletion();
122 | const tools = choices[0].message.tool_calls;
123 | return tools ?? [];
124 | }
125 |
126 | /**
127 | * Retrieves tools from a readable stream asynchronously.
128 | *
129 | * @param stream - The readable stream to extract tools from
130 | * @returns A promise that resolves with the tools extracted from the stream
131 | */
132 | export async function getToolsFromRunStream(stream: ReadableStream) {
133 | return getToolsFromStream(stream);
134 | }
135 |
136 | /**
137 | * Extracts tool calls from non-stream response.
138 | * @param response - The run response object
139 | * @returns A promise that resolves to an array of tool calls. Returns empty array if no tools are present.
140 | */
141 | export async function getToolsFromRun(
142 | response: RunResponse,
143 | ): Promise {
144 | const tools = response.choices[0].message.tool_calls;
145 | return tools ?? [];
146 | }
147 |
--------------------------------------------------------------------------------
/packages/langbase/src/lib/utils/doc-to-formdata.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Converts various document formats to FormData.
3 | *
4 | * @param options - The conversion options
5 | * @param options.document - The document to convert. Can be Buffer, File, FormData or ReadableStream
6 | * @param options.documentName - The name of the document
7 | * @param options.contentType - The MIME type of the document
8 | *
9 | * @returns A Promise that resolves to FormData containing the document
10 | *
11 | * @example
12 | * ```ts
13 | * const buffer = Buffer.from('Hello World');
14 | * const formData = await convertDocToFormData({
15 | * document: buffer,
16 | * documentName: 'hello.txt',
17 | * contentType: 'text/plain'
18 | * });
19 | * ```
20 | */
21 | export async function convertDocToFormData(options: {
22 | document: Buffer | File | FormData | ReadableStream;
23 | documentName: string;
24 | contentType: string;
25 | }) {
26 | let formData = new FormData();
27 |
28 | if (options.document instanceof Buffer) {
29 | const documentBlob = new Blob([options.document], {
30 | type: options.contentType,
31 | });
32 | formData.append('document', documentBlob, options.documentName);
33 | } else if (options.document instanceof File) {
34 | formData.append('document', options.document, options.documentName);
35 | } else if (options.document instanceof FormData) {
36 | formData = options.document;
37 | } else if (options.document instanceof ReadableStream) {
38 | const chunks: Uint8Array[] = [];
39 | const reader = options.document.getReader();
40 |
41 | while (true) {
42 | const {done, value} = await reader.read();
43 | if (done) break;
44 | chunks.push(value);
45 | }
46 |
47 | const documentBlob = new Blob(chunks, {type: options.contentType});
48 | formData.append('document', documentBlob, options.documentName);
49 | }
50 |
51 | formData.append('documentName', options.documentName);
52 |
53 | return formData;
54 | }
55 |
--------------------------------------------------------------------------------
/packages/langbase/src/pipes/pipes.test.ts:
--------------------------------------------------------------------------------
1 | import {beforeEach, describe, expect, it, vi} from 'vitest';
2 | import {GenerateOptions, GenerateResponse, Pipe, StreamResponse} from './pipes';
3 |
4 | // Mock the Request class
5 | vi.mock('../common/request');
6 |
7 | describe('Pipe', () => {
8 | let pipe: Pipe;
9 | const mockApiKey = 'test-api-key';
10 |
11 | beforeEach(() => {
12 | pipe = new Pipe({apiKey: mockApiKey});
13 | vi.resetAllMocks();
14 | });
15 |
16 | describe('generateText', () => {
17 | it('should call request.post with correct parameters for non-chat generation', async () => {
18 | const mockOptions: GenerateOptions = {
19 | messages: [{role: 'user', content: 'Hello'}],
20 | };
21 | const mockResponse: GenerateResponse = {
22 | completion: 'Hello, how can I help you?',
23 | raw: {
24 | id: 'test-id',
25 | object: 'test-object',
26 | created: 123456789,
27 | model: 'test-model',
28 | choices: [
29 | {
30 | index: 0,
31 | message: {
32 | role: 'assistant',
33 | content: 'Hello, how can I help you?',
34 | },
35 | logprobs: null,
36 | finish_reason: 'stop',
37 | },
38 | ],
39 | usage: {
40 | prompt_tokens: 5,
41 | completion_tokens: 10,
42 | total_tokens: 15,
43 | },
44 | system_fingerprint: null,
45 | },
46 | };
47 |
48 | const mockPost = vi.fn().mockResolvedValue(mockResponse);
49 | (pipe as any).request = {post: mockPost};
50 |
51 | const result = await pipe.generateText(mockOptions);
52 |
53 | expect(mockPost).toHaveBeenCalledWith({
54 | endpoint: '/beta/generate',
55 | body: {...mockOptions, stream: false},
56 | });
57 | expect(result).toEqual(mockResponse);
58 | });
59 |
60 | it('should call request.post with correct parameters for chat generation', async () => {
61 | const mockOptions: GenerateOptions = {
62 | messages: [{role: 'user', content: 'Hello'}],
63 | chat: true,
64 | };
65 | const mockResponse: GenerateResponse = {
66 | completion: 'Hello! How can I assist you today?',
67 | threadId: 'chat-thread-123',
68 | raw: {
69 | id: 'chat-id',
70 | object: 'chat-object',
71 | created: 123456789,
72 | model: 'chat-model',
73 | choices: [
74 | {
75 | index: 0,
76 | message: {
77 | role: 'assistant',
78 | content: 'Hello! How can I assist you today?',
79 | },
80 | logprobs: null,
81 | finish_reason: 'stop',
82 | },
83 | ],
84 | usage: {
85 | prompt_tokens: 5,
86 | completion_tokens: 12,
87 | total_tokens: 17,
88 | },
89 | system_fingerprint: null,
90 | },
91 | };
92 |
93 | const mockPost = vi.fn().mockResolvedValue(mockResponse);
94 | (pipe as any).request = {post: mockPost};
95 |
96 | const result = await pipe.generateText(mockOptions);
97 |
98 | expect(mockPost).toHaveBeenCalledWith({
99 | endpoint: '/beta/chat',
100 | body: {...mockOptions, stream: false},
101 | });
102 | expect(result).toEqual(mockResponse);
103 | });
104 | });
105 |
106 | describe('streamText', () => {
107 | it('should call request.post with correct parameters for non-chat streaming', async () => {
108 | const mockOptions: GenerateOptions = {
109 | messages: [{role: 'user', content: 'Hello'}],
110 | };
111 | const mockStreamResponse: StreamResponse = {
112 | stream: {} as any,
113 | threadId: null,
114 | };
115 |
116 | const mockPost = vi.fn().mockResolvedValue(mockStreamResponse);
117 | (pipe as any).request = {post: mockPost};
118 |
119 | const result = await pipe.streamText(mockOptions);
120 |
121 | expect(mockPost).toHaveBeenCalledWith({
122 | endpoint: '/beta/generate',
123 | body: {...mockOptions, stream: true},
124 | });
125 | expect(result).toEqual(mockStreamResponse);
126 | });
127 |
128 | it('should call request.post with correct parameters for chat streaming', async () => {
129 | const mockOptions: GenerateOptions = {
130 | messages: [{role: 'user', content: 'Hello'}],
131 | chat: true,
132 | };
133 | const mockStreamResponse: StreamResponse = {
134 | stream: {} as any,
135 | threadId: 'chat-thread-123',
136 | };
137 |
138 | const mockPost = vi.fn().mockResolvedValue(mockStreamResponse);
139 | (pipe as any).request = {post: mockPost};
140 |
141 | const result = await pipe.streamText(mockOptions);
142 |
143 | expect(mockPost).toHaveBeenCalledWith({
144 | endpoint: '/beta/chat',
145 | body: {...mockOptions, stream: true},
146 | });
147 | expect(result).toEqual(mockStreamResponse);
148 | });
149 | });
150 | });
151 |
--------------------------------------------------------------------------------
/packages/langbase/src/pipes/pipes.ts:
--------------------------------------------------------------------------------
1 | import {Message, Role, ToolCall, Usage, Variable} from '@/langbase/langbase';
2 | import {Request} from '../common/request';
3 | import {Stream} from '../common/stream';
4 |
5 | export interface GenerateOptions {
6 | messages?: Message[];
7 | variables?: Variable[];
8 | threadId?: string;
9 | chat?: boolean;
10 | }
11 |
12 | export interface StreamOptions {
13 | messages?: Message[];
14 | variables?: Variable[];
15 | threadId?: string | null;
16 | chat?: boolean;
17 | }
18 |
19 | interface ChoiceGenerate {
20 | index: number;
21 | message: Message;
22 | logprobs: boolean | null;
23 | finish_reason: string;
24 | }
25 |
26 | interface ChoiceStream {
27 | index: number;
28 | delta: Delta;
29 | logprobs: boolean | null;
30 | finish_reason: string;
31 | }
32 |
33 | interface Delta {
34 | role?: Role;
35 | content?: string;
36 | tool_calls?: ToolCall[];
37 | }
38 |
39 | export interface GenerateResponse {
40 | completion: string;
41 | threadId?: string;
42 | id: string;
43 | object: string;
44 | created: number;
45 | model: string;
46 | choices: ChoiceGenerate[];
47 | usage: Usage;
48 | system_fingerprint: string | null;
49 | }
50 |
51 | export type StreamText = Stream;
52 |
53 | export interface StreamResponse {
54 | stream: StreamText;
55 | threadId: string | null;
56 | }
57 |
58 | export interface StreamChunk {
59 | id: string;
60 | object: string;
61 | created: number;
62 | model: string;
63 | choices: ChoiceStream[];
64 | }
65 |
66 | export interface PipeOptions {
67 | apiKey: string;
68 | baseUrl?: string;
69 | name?: string;
70 | }
71 |
72 | export class Pipe {
73 | private request: Request;
74 |
75 | constructor(options: PipeOptions) {
76 | const baseUrl = 'https://api.langbase.com';
77 | this.request = new Request({apiKey: options.apiKey, baseUrl});
78 | }
79 |
80 | /**
81 | * @deprecated This method is deprecated and will be removed in a future version.
82 | *
83 | * Please use `langbase.pipes.run()` instead
84 | * @see https://langbase.com/docs/sdk/pipe/run
85 | */
86 | async generateText(options: GenerateOptions): Promise {
87 | return this.request.post({
88 | endpoint: options.chat ? '/beta/chat' : '/beta/generate',
89 | body: {...options, stream: false},
90 | });
91 | }
92 |
93 | /**
94 | * @deprecated This method is deprecated and will be removed in a future version.
95 | *
96 | * Please use `langbase.pipes.run()` instead
97 | * @see https://langbase.com/docs/sdk/pipe/run
98 | */
99 | async streamText(options: StreamOptions): Promise {
100 | return this.request.post({
101 | endpoint: options.chat ? '/beta/chat' : '/beta/generate',
102 | body: {...options, stream: true},
103 | });
104 | }
105 | }
106 |
107 | /**
108 | * Print stream to standard output (console).
109 | * @param stream The stream to print
110 | */
111 | export const printStreamToStdout = async (
112 | stream: StreamText,
113 | ): Promise => {
114 | for await (const chunk of stream) {
115 | const textPart = chunk.choices[0]?.delta?.content || '';
116 | process.stdout.write(textPart);
117 | }
118 | };
119 |
--------------------------------------------------------------------------------
/packages/langbase/src/react/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-pipe';
2 |
--------------------------------------------------------------------------------
/packages/langbase/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./node_modules/@langbase/tsconfig/react-library.json",
3 | "include": ["."],
4 | "exclude": ["*/dist", "dist", "build", "node_modules"],
5 | "compilerOptions": {
6 | "target": "ES2018",
7 | "stripInternal": true,
8 | "lib": ["dom", "dom.iterable", "esnext"],
9 | "types": ["@types/node", "vitest/globals"],
10 | "baseUrl": "." /* Specify the base directory to resolve non-relative module names. */,
11 | "paths": {
12 | "@/*": ["src/*"] // Maps all imports starting with "@" to the src folder
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/langbase/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'tsup';
2 |
3 | export default defineConfig([
4 | // Universal APIs
5 | {
6 | entry: ['src/index.ts'],
7 | outDir: 'dist',
8 | format: ['cjs', 'esm'],
9 | external: ['react', 'svelte', 'vue'],
10 | dts: true,
11 | clean: true,
12 | sourcemap: true,
13 | },
14 | // React APIs
15 | {
16 | entry: ['src/react/index.ts'],
17 | outDir: 'react/dist',
18 | banner: {
19 | js: "'use client'",
20 | },
21 | format: ['cjs', 'esm'],
22 | external: ['react', 'svelte', 'vue', 'solid-js'],
23 | dts: {
24 | entry: 'src/react/index.ts',
25 | },
26 | clean: true,
27 | sourcemap: true,
28 | },
29 | ]);
30 |
--------------------------------------------------------------------------------
/packages/langbase/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "//"
4 | ],
5 | "tasks": {
6 | "build": {
7 | "outputs": [
8 | "**/dist/**"
9 | ]
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/langbase/types/index.ts:
--------------------------------------------------------------------------------
1 | export type Headers = Record;
2 |
--------------------------------------------------------------------------------
/packages/langbase/vitest.edge.config.js:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vite';
2 |
3 | // https://vitejs.dev/config/
4 | export default defineConfig({
5 | test: {
6 | environment: 'edge-runtime',
7 | globals: true,
8 | include: ['**/*.test.ts{,x}'],
9 | exclude: [
10 | '**/*.ui.test.ts{,x}',
11 | '**/*.e2e.test.ts{,x}',
12 | '**/node_modules/**',
13 | ],
14 | typecheck: {
15 | enabled: true,
16 | },
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/packages/langbase/vitest.node.config.js:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vite';
2 |
3 | // https://vitejs.dev/config/
4 | export default defineConfig({
5 | test: {
6 | environment: 'node',
7 | globals: true,
8 | include: ['**/*.test.ts{,x}'],
9 | exclude: [
10 | '**/*.ui.test.ts{,x}',
11 | '**/*.e2e.test.ts{,x}',
12 | '**/node_modules/**',
13 | ],
14 | typecheck: {
15 | enabled: true,
16 | },
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/packages/langbase/vitest.ui.react.config.js:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react';
2 | import {defineConfig} from 'vite';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | test: {
8 | environment: 'jsdom',
9 | globals: true,
10 | include: ['rsc/**/*.ui.test.ts{,x}'],
11 | exclude: ['**/node_modules/**'],
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'apps/*'
3 | - 'packages/*'
4 | - 'tools/*'
5 | - 'examples/*'
6 |
--------------------------------------------------------------------------------
/tools/eslint-config/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @langbase/eslint-config
2 |
3 | ## 1.0.0
4 |
5 | ### Major Changes
6 |
7 | - ‼️ BREAKING: [`generateText()`](https://langbase.com/docs/langbase-sdk/generate-text) now doesn't return raw instead all properties are included in the main response.
8 |
9 | #### BEFORE
10 |
11 | ```ts
12 | interface GenerateNonStreamResponse {
13 | completion: string;
14 | raw: {
15 | id: string;
16 | object: string;
17 | created: number;
18 | model: string;
19 | choices: ChoiceNonStream[];
20 | usage: Usage;
21 | system_fingerprint: string | null;
22 | };
23 | }
24 | ```
25 |
26 | #### NOW
27 |
28 | ```ts
29 | interface GenerateResponse {
30 | completion: string;
31 | threadId?: string;
32 | id: string;
33 | object: string;
34 | created: number;
35 | model: string;
36 | system_fingerprint: string | null;
37 | choices: ChoiceGenerate[];
38 | usage: Usage;
39 | }
40 | ```
41 |
42 | ‼️ BREAKING: `ChoiceNonStream` type is now renamed to `ChoiceGenerate`.
43 |
44 | ‼️ BREAKING: [`streamText()`](https://langbase.com/docs/langbase-sdk/stream-text) now returns a threadId and stream as an object instead of returning stream alone.
45 |
46 | #### BEFORE
47 |
48 | ```ts
49 | const stream = await pipe.streamText({
50 | messages: [{role: 'user', content: 'Who is an AI Engineer?'}],
51 | });
52 | ```
53 |
54 | #### NOW
55 |
56 | ```ts
57 | const {threadId, stream} = await pipe.streamText({
58 | messages: [{role: 'user', content: 'Who is an AI Engineer?'}],
59 | });
60 | ```
61 |
62 | 📦 NEW: Chat support in both both [`generateText()`](https://langbase.com/docs/langbase-sdk/generate-text) and [`streamText()`](https://langbase.com/docs/langbase-sdk/stream-text)
63 | 👌 IMPROVE: Example updates for Node, browser, Next.js, React, etc.
64 | 👌 IMPROVE: ⌘ Langbase [SDK Docs](https://langbase.com/docs/langbase-sdk) and API reference for both [`generateText()`](https://langbase.com/docs/langbase-sdk/generate-text) and [`streamText()`](https://langbase.com/docs/langbase-sdk/stream-text)
65 |
66 | ## 0.1.0
67 |
68 | ### Minor Changes
69 |
70 | - b026a61: Initial beta release
71 |
--------------------------------------------------------------------------------
/tools/eslint-config/README.md:
--------------------------------------------------------------------------------
1 | # `@turbo/eslint-config`
2 |
3 | Collection of internal eslint configurations.
4 |
--------------------------------------------------------------------------------
/tools/eslint-config/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['next', 'turbo', 'prettier'],
3 | rules: {
4 | '@next/next/no-html-link-for-pages': 'off',
5 | },
6 | parserOptions: {
7 | babelOptions: {
8 | presets: [require.resolve('next/babel')],
9 | },
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/tools/eslint-config/library.js:
--------------------------------------------------------------------------------
1 | const {resolve} = require('node:path');
2 |
3 | const project = resolve(process.cwd(), 'tsconfig.json');
4 |
5 | /** @type {import("eslint").Linter.Config} */
6 | module.exports = {
7 | extends: ['eslint:recommended', 'prettier', 'turbo'],
8 | plugins: ['only-warn'],
9 | globals: {
10 | React: true,
11 | JSX: true,
12 | },
13 | env: {
14 | node: true,
15 | },
16 | settings: {
17 | 'import/resolver': {
18 | typescript: {
19 | project,
20 | },
21 | },
22 | },
23 | ignorePatterns: [
24 | // Ignore dotfiles
25 | '.*.js',
26 | 'node_modules/',
27 | 'dist/',
28 | ],
29 | overrides: [
30 | {
31 | files: ['*.js?(x)', '*.ts?(x)'],
32 | },
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/tools/eslint-config/next.js:
--------------------------------------------------------------------------------
1 | const {resolve} = require('node:path');
2 |
3 | const project = resolve(process.cwd(), 'tsconfig.json');
4 |
5 | /** @type {import("eslint").Linter.Config} */
6 | module.exports = {
7 | extends: [
8 | 'eslint:recommended',
9 | 'prettier',
10 | require.resolve('@vercel/style-guide/eslint/next'),
11 | 'turbo',
12 | ],
13 | globals: {
14 | React: true,
15 | JSX: true,
16 | },
17 | env: {
18 | node: true,
19 | },
20 | plugins: ['only-warn'],
21 | settings: {
22 | 'import/resolver': {
23 | typescript: {
24 | project,
25 | },
26 | },
27 | },
28 | ignorePatterns: [
29 | // Ignore dotfiles
30 | '.*.js',
31 | 'node_modules/',
32 | ],
33 | overrides: [{files: ['*.js?(x)', '*.ts?(x)']}],
34 | };
35 |
--------------------------------------------------------------------------------
/tools/eslint-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@langbase/eslint-config",
3 | "version": "1.0.0",
4 | "license": "Apache-2.0",
5 | "private": true,
6 | "files": [
7 | "library.js",
8 | "next.js",
9 | "react-internal.js"
10 | ],
11 | "devDependencies": {
12 | "@next/eslint-plugin-next": "^14.1.4",
13 | "@typescript-eslint/eslint-plugin": "^7.1.0",
14 | "@typescript-eslint/parser": "^7.1.0",
15 | "@vercel/style-guide": "^5.2.0",
16 | "eslint-config-prettier": "^9.1.0",
17 | "eslint-config-turbo": "^2.0.0",
18 | "eslint-plugin-only-warn": "^1.1.0",
19 | "typescript": "^5.3.3"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tools/eslint-config/react-internal.js:
--------------------------------------------------------------------------------
1 | const {resolve} = require('node:path');
2 |
3 | const project = resolve(process.cwd(), 'tsconfig.json');
4 |
5 | /*
6 | * This is a custom ESLint configuration for use with
7 | * internal (bundled by their consumer) libraries
8 | * that utilize React.
9 | */
10 |
11 | /** @type {import("eslint").Linter.Config} */
12 | module.exports = {
13 | extends: ['eslint:recommended', 'prettier', 'turbo'],
14 | plugins: ['only-warn'],
15 | globals: {
16 | React: true,
17 | JSX: true,
18 | },
19 | env: {
20 | browser: true,
21 | },
22 | settings: {
23 | 'import/resolver': {
24 | typescript: {
25 | project,
26 | },
27 | },
28 | },
29 | ignorePatterns: [
30 | // Ignore dotfiles
31 | '.*.js',
32 | 'node_modules/',
33 | 'dist/',
34 | ],
35 | overrides: [
36 | // Force ESLint to detect .tsx files
37 | {files: ['*.js?(x)', '*.ts?(x)']},
38 | ],
39 | };
40 |
--------------------------------------------------------------------------------
/tools/tsconfig/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @langbase/tsconfig
2 |
3 | ## 1.0.0
4 |
5 | ### Major Changes
6 |
7 | - ‼️ BREAKING: [`generateText()`](https://langbase.com/docs/langbase-sdk/generate-text) now doesn't return raw instead all properties are included in the main response.
8 |
9 | #### BEFORE
10 |
11 | ```ts
12 | interface GenerateNonStreamResponse {
13 | completion: string;
14 | raw: {
15 | id: string;
16 | object: string;
17 | created: number;
18 | model: string;
19 | choices: ChoiceNonStream[];
20 | usage: Usage;
21 | system_fingerprint: string | null;
22 | };
23 | }
24 | ```
25 |
26 | #### NOW
27 |
28 | ```ts
29 | interface GenerateResponse {
30 | completion: string;
31 | threadId?: string;
32 | id: string;
33 | object: string;
34 | created: number;
35 | model: string;
36 | system_fingerprint: string | null;
37 | choices: ChoiceGenerate[];
38 | usage: Usage;
39 | }
40 | ```
41 |
42 | ‼️ BREAKING: `ChoiceNonStream` type is now renamed to `ChoiceGenerate`.
43 |
44 | ‼️ BREAKING: [`streamText()`](https://langbase.com/docs/langbase-sdk/stream-text) now returns a threadId and stream as an object instead of returning stream alone.
45 |
46 | #### BEFORE
47 |
48 | ```ts
49 | const stream = await pipe.streamText({
50 | messages: [{role: 'user', content: 'Who is an AI Engineer?'}],
51 | });
52 | ```
53 |
54 | #### NOW
55 |
56 | ```ts
57 | const {threadId, stream} = await pipe.streamText({
58 | messages: [{role: 'user', content: 'Who is an AI Engineer?'}],
59 | });
60 | ```
61 |
62 | 📦 NEW: Chat support in both both [`generateText()`](https://langbase.com/docs/langbase-sdk/generate-text) and [`streamText()`](https://langbase.com/docs/langbase-sdk/stream-text)
63 | 👌 IMPROVE: Example updates for Node, browser, Next.js, React, etc.
64 | 👌 IMPROVE: ⌘ Langbase [SDK Docs](https://langbase.com/docs/langbase-sdk) and API reference for both [`generateText()`](https://langbase.com/docs/langbase-sdk/generate-text) and [`streamText()`](https://langbase.com/docs/langbase-sdk/stream-text)
65 |
66 | ## 0.1.0
67 |
68 | ### Minor Changes
69 |
70 | - b026a61: Initial beta release
71 |
--------------------------------------------------------------------------------
/tools/tsconfig/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "strict": true,
6 | "composite": false,
7 | "declaration": true,
8 | "skipLibCheck": true,
9 | "inlineSources": false,
10 | "declarationMap": true,
11 | "esModuleInterop": true,
12 | "noUnusedLocals": false,
13 | "isolatedModules": true,
14 | "moduleResolution": "node",
15 | "noUnusedParameters": false,
16 | "preserveWatchOutput": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "types": ["@types/node", "vitest/globals"]
19 | },
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------
/tools/tsconfig/nextjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Next.js",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "target": "es5",
7 | "noEmit": true,
8 | "allowJs": true,
9 | "module": "esnext",
10 | "jsx": "preserve",
11 | "incremental": true,
12 | "declaration": false,
13 | "declarationMap": false,
14 | "resolveJsonModule": true,
15 | "moduleResolution": "Bundler",
16 | "lib": ["dom", "dom.iterable", "esnext"]
17 | },
18 | "include": ["src", "next-env.d.ts"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/tools/tsconfig/node14.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Node 14",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "lib": ["ES2020"],
7 | "module": "commonjs",
8 | "target": "ES2020"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tools/tsconfig/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@langbase/tsconfig",
3 | "version": "1.0.0",
4 | "private": true,
5 | "license": "Apache-2.0",
6 | "publishConfig": {
7 | "access": "public"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tools/tsconfig/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx",
7 | "lib": ["dom", "ES2015"],
8 | "module": "ESNext",
9 | "target": "es6"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "globalEnv": [
4 | "CI",
5 | "PORT"
6 | ],
7 | "tasks": {
8 | "build": {
9 | "dependsOn": [
10 | "^build"
11 | ],
12 | "env": [
13 | "ANTHROPIC_API_KEY",
14 | "AWS_REGION",
15 | "AWS_ACCESS_KEY_ID",
16 | "AWS_SECRET_ACCESS_KEY",
17 | "COHERE_API_KEY",
18 | "FIREWORKS_API_KEY",
19 | "GOOGLE_API_KEY",
20 | "GROQ_API_KEY",
21 | "HUGGINGFACE_API_KEY",
22 | "MISTRAL_API_KEY",
23 | "OPENAI_API_KEY",
24 | "OPENAI_API_BASE",
25 | "PERPLEXITY_API_KEY",
26 | "REPLICATE_API_KEY",
27 | "NODE_ENV",
28 | "ASSISTANT_ID",
29 | "INKEEP_API_KEY",
30 | "INKEEP_INTEGRATION_ID",
31 | "VERCEL_URL"
32 | ],
33 | "outputs": [
34 | "dist/**",
35 | ".next/**",
36 | "!.next/cache/**"
37 | ]
38 | },
39 | "lint": {
40 | "dependsOn": [
41 | "^lint"
42 | ]
43 | },
44 | "type-check": {
45 | "dependsOn": [
46 | "^build",
47 | "build"
48 | ]
49 | },
50 | "test": {
51 | "dependsOn": [
52 | "^build",
53 | "build"
54 | ]
55 | },
56 | "publint": {
57 | "dependsOn": [
58 | "^build",
59 | "build"
60 | ]
61 | },
62 | "clean": {
63 | "dependsOn": [
64 | "^clean"
65 | ]
66 | },
67 | "dev": {
68 | "cache": false,
69 | "persistent": true
70 | },
71 | "prettier-check": {},
72 | "integration-test": {
73 | "dependsOn": [
74 | "^build",
75 | "build"
76 | ]
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------