├── .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 |
handleSubmit(e, {allowEmptySubmit: true})} 132 | className="flex space-x-2" 133 | > 134 | 142 | 143 |
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 |
61 | setPrompt(e.target.value)} 66 | required 67 | /> 68 | 69 | 72 |
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 |
70 | setPrompt(e.target.value)} 74 | value={prompt} 75 | required 76 | /> 77 | 80 |
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 |
61 | setPrompt(e.target.value)} 66 | required 67 | /> 68 | 69 | 72 |
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 |
70 | setPrompt(e.target.value)} 74 | value={prompt} 75 | required 76 | /> 77 | 80 |
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 | --------------------------------------------------------------------------------