diff --git a/README.md b/README.md index 7d21b3f0..6d338c19 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# [`nlux`](https://nlux.ai) ๐ŸŒฒโœจ๐Ÿ’ฌ +# [nlux](https://nlux.ai) ๐ŸŒฒโœจ๐Ÿ’ฌ ![Free And Open Source](https://img.shields.io/badge/Free%20%26%20Open%20Source-1ccb61) [![npm version](https://img.shields.io/badge/NPM-@nlux/react-1ccb61)](https://www.npmjs.com/package/@nlux/react) [![Docs nlux.ai](https://img.shields.io/badge/Docs_Website-nlux.dev-fa896b)](https://nlux.dev) -## The JS / React Library For Building Conversational AI Interfaces โœจ๐Ÿ’ฌ +## The React + JS Library For Building Conversational AI Interfaces โœจ๐Ÿ’ฌ [![nlux UI For Any LLM](https://nlux.ai/images/github/nlux-ui-for-llms-banner.gif)](https://nlux.dev) -`nlux` (for Natural Language User Experience) is an open-source Javascript library that makes it super simple to +`nlux` (_for Natural Language User Experience_) is an open-source Javascript library that makes it super simple to integrate powerful large language models (LLMs) like ChatGPT into your web app or website. With just a few lines of code, you can add conversational AI capabilities and interact with your favourite LLM. @@ -20,8 +20,10 @@ of code, you can add conversational AI capabilities and interact with your favou * A flexible interface to **Create Your Own Adapter** for any LLM or API, with support for stream or fetch modes. * **Bot and User Personas** โ€• Customize the bot and user personas with names, images, and descriptions. * **Streaming LLM Output** โ€• Stream the chat response to the UI as it's being generated. -* **High Customizability** - Tune almost every UI aspect through theming, layout options, and more. -* **Zero Dependencies** โ€• Lightweight codebase โ€• **Core** with zero dependencies and no external UI libraries. +* **High Customizability** โ€• Tune almost every UI aspect through theming, layout options, and more. +* **`nlbridge` Integration** โ€• Seamless integration with [nlbridge](https://www.npmjs.com/package/@nlbridge/express), + the LLM middleware library by the same team. +* **Zero Dependencies** โ€• Lightweight codebase โ€• Core with zero dependencies and no external UI libraries. [![200+ Unit Tests](https://github.com/nluxai/nlux/actions/workflows/run-all-tests.yml/badge.svg)](https://github.com/nluxai/nlux/actions/workflows/run-all-tests.yml) @@ -35,7 +37,10 @@ It is a monorepo that contains the following NPM packages: * [`@nlux/react`](https://www.npmjs.com/package/@nlux/react) โ€• React JS components for `nlux`. * [`@nlux/langchain-react`](https://www.npmjs.com/package/@nlux/langchain-react) โ€• React hooks and adapter for APIs created using LangChain's LangServe library. -* [`@nlux/openai-react`](https://www.npmjs.com/package/@nlux/openai-react) โ€• React hooks for the OpenAI API. +* [`@nlux/nlbridge-react`](https://www.npmjs.com/package/@nlux/nlbridge-react) โ€• Integration with + `nlbridge`, the LLM middleware library by the same team. +* [`@nlux/openai-react`](https://www.npmjs.com/package/@nlux/openai-react) โ€• React hooks for the OpenAI API, for testing + and development. * [`@nlux/hf-react`](https://www.npmjs.com/package/@nlux/hf-react) โ€• React hooks and pre-processors for the Hugging Face Inference API @@ -44,7 +49,10 @@ It is a monorepo that contains the following NPM packages: * [`@nlux/core`](https://www.npmjs.com/package/@nlux/core) โ€• The core Vanilla JS library to use with any web framework. * [`@nlux/langchain`](https://www.npmjs.com/package/@nlux/langchain) โ€• Adapter for APIs created using LangChain's LangServe library. -* [`@nlux/openai`](https://www.npmjs.com/package/@nlux/openai) โ€• Adapter for the OpenAI API. +* [`@nlux/nlbridge`](https://www.npmjs.com/package/@nlux/nlbridge) โ€• Integration with + `nlbridge`, the LLM middleware library by the same team. +* [`@nlux/openai`](https://www.npmjs.com/package/@nlux/openai) โ€• Adapter for the OpenAI API, for testing and + development. * [`@nlux/hf`](https://www.npmjs.com/package/@nlux/hf) โ€• Adapter and pre-processors for the Hugging Face Inference API. **Themes & Extensions:** @@ -59,7 +67,8 @@ Please visit each package's NPM page for information on how to use it. ## Docs & Examples ๐Ÿคฉ -* For developer documentation, examples, and API reference โ€• please visit: **[nlux.dev](https://nlux.dev/)** +* For developer documentation, examples, and API reference โ€• please visit: + [nlux.dev](https://nlux.dev/) ## Design Principles โšœ๏ธ @@ -91,11 +100,11 @@ cross platforms, with a focus on performance and usability. * **Star The Repo** ๐ŸŒŸ โ€• If you like `nlux`, please star the repo to show your support. Your support is what keeps this open-source project going ๐Ÿงก -* **[GitHub Discussions](https://github.com/nluxai/nlux/discussions)** โ€• Ask questions, report issues, and share your +* [GitHub Discussions](https://github.com/nluxai/nlux/discussions) โ€• Ask questions, report issues, and share your ideas with the community. -* **[Discord Community](https://discord.gg/SRwDmZghNB)** โ€• Join our Discord server to chat with the community and get +* [Discord Community](https://discord.gg/SRwDmZghNB) โ€• Join our Discord server to chat with the community and get support. -* **[nlux.dev](https://nlux.dev/)** Developer Website โ€• Examples, learning resources, and API reference. +* [nlux.dev](https://nlux.dev/) Developer Website โ€• Examples, learning resources, and API reference. ## License ๐Ÿ“ƒ diff --git a/packages/js/core/src/index.ts b/packages/js/core/src/index.ts index 60524082..1da3d751 100644 --- a/packages/js/core/src/index.ts +++ b/packages/js/core/src/index.ts @@ -42,9 +42,6 @@ export type { export type { StandardAdapter, - StandardAdapterEvent, - StandardAdapterStatus, - StandardAdapterEventData, } from './types/standardAdapter'; export type { diff --git a/packages/js/core/src/types/standardAdapter.ts b/packages/js/core/src/types/standardAdapter.ts index 99cba0a2..823a0122 100644 --- a/packages/js/core/src/types/standardAdapter.ts +++ b/packages/js/core/src/types/standardAdapter.ts @@ -1,20 +1,6 @@ import {Adapter, AdapterExtras, DataTransferMode, StreamingAdapterObserver} from './adapter'; import {StandardAdapterConfig, StandardAdapterInfo} from './standardAdapterConfig'; -export type StandardAdapterStatus = 'disconnected' - | 'connecting' - | 'connected' - | 'disconnecting' - | 'idle' - | 'error'; - -export type StandardAdapterEvent = 'state-change' - | 'message-received' - | 'message-sent' - | 'chunk-received'; - -export type StandardAdapterEventData = string | StandardAdapterStatus; - export interface StandardAdapter extends Adapter { get config(): StandardAdapterConfig; get dataTransferMode(): DataTransferMode; @@ -23,7 +9,6 @@ export interface StandardAdapter extends Adapte fetchText(message: string, extras: AdapterExtras): Promise; get id(): string; get info(): StandardAdapterInfo; - get status(): StandardAdapterStatus; streamText(message: string, observer: StreamingAdapterObserver, extras: AdapterExtras): void; } @@ -34,6 +19,5 @@ export const isStandardAdapter = (adapter: Adapter): boolean => { && 'encode' in adapter && 'id' in adapter && 'info' in adapter - && 'status' in adapter && ('fetchText' in adapter || 'streamText' in adapter); }; diff --git a/packages/js/hf/src/hf/adapter/adapter.ts b/packages/js/hf/src/hf/adapter/adapter.ts index f460bf91..84e2e5e3 100644 --- a/packages/js/hf/src/hf/adapter/adapter.ts +++ b/packages/js/hf/src/hf/adapter/adapter.ts @@ -6,7 +6,6 @@ import { StandardAdapter, StandardAdapterConfig, StandardAdapterInfo, - StandardAdapterStatus, StreamingAdapterObserver, uid, warn, @@ -70,10 +69,6 @@ export class HfAdapterImpl implements StandardAdapter { }; } - get status(): StandardAdapterStatus { - return 'idle'; - } - async decode(payload: any): Promise { const output = (() => { if (typeof payload === 'string') { diff --git a/packages/js/langchain/src/langserve/adapter/adapter.ts b/packages/js/langchain/src/langserve/adapter/adapter.ts index 53b35fb7..763c98a3 100644 --- a/packages/js/langchain/src/langserve/adapter/adapter.ts +++ b/packages/js/langchain/src/langserve/adapter/adapter.ts @@ -5,7 +5,6 @@ import { StandardAdapter, StandardAdapterConfig, StandardAdapterInfo, - StandardAdapterStatus, StreamingAdapterObserver, uid, warn, @@ -100,10 +99,6 @@ export abstract class LangServeAbstractAdapter implements StandardAdapter Promise = async () => ([ + { + input: './src/index.ts', + logLevel: 'silent', + treeshake: 'smallest', + strictDeprecations: true, + plugins: [ + commonjs(), + esbuild(), + isProduction && strip({ + include: '**/*.(mjs|js|ts)', + functions: ['debug', 'console.log', 'console.info'], + }), + !isProduction && nodeResolve(), + replace({ + values: { + 'process.env.NLUX_DEBUG_ENABLED': JSON.stringify(isProduction ? 'false' : 'true'), + }, + preventAssignment: true, + }), + isProduction && terser(), + ], + external: [ + '@nlux/core', + ], + output: generateOutputConfig(packageName, outputFile, isProduction), + }, + generateDts(outputFile, isProduction), +]); + +export default packageConfig; diff --git a/packages/js/nlbridge/src/index.ts b/packages/js/nlbridge/src/index.ts new file mode 100644 index 00000000..85c7eb8f --- /dev/null +++ b/packages/js/nlbridge/src/index.ts @@ -0,0 +1,14 @@ +export type { + Adapter, + StandardAdapter, + StreamingAdapterObserver, + DataTransferMode, +} from '@nlux/core'; + +export {debug} from '@nlux/core'; + +export type {NlBridgeAdapterOptions} from './nlbridge/types/adapterOptions'; + +export type {NlBridgeAdapterBuilder} from './nlbridge/builder/builder'; + +export {createAdapter} from './nlbridge/builder/createAdapter'; diff --git a/packages/js/nlbridge/src/nlbridge/adapter/adapter.ts b/packages/js/nlbridge/src/nlbridge/adapter/adapter.ts new file mode 100644 index 00000000..124d1e6a --- /dev/null +++ b/packages/js/nlbridge/src/nlbridge/adapter/adapter.ts @@ -0,0 +1,76 @@ +import { + AdapterExtras, + DataTransferMode, + StandardAdapter, + StandardAdapterConfig, + StandardAdapterInfo, + StreamingAdapterObserver, + uid, +} from '@nlux/core'; +import {NlBridgeAdapterOptions} from '../types/adapterOptions'; + +export abstract class NlBridgeAbstractAdapter implements StandardAdapter { + static defaultDataTransferMode: DataTransferMode = 'stream'; + + private readonly __instanceId: string; + private readonly __options: NlBridgeAdapterOptions; + + private readonly theDataTransferModeToUse: DataTransferMode; + private readonly theEndpointUrlToUse: string; + + constructor(options: NlBridgeAdapterOptions) { + this.__instanceId = `${this.info.id}-${uid()}`; + this.__options = {...options}; + + this.theDataTransferModeToUse = options.dataTransferMode ?? NlBridgeAbstractAdapter.defaultDataTransferMode; + this.theEndpointUrlToUse = options.url; + } + + get config(): StandardAdapterConfig { + return { + encodeMessage: (message: string) => { + return Promise.resolve(message); + }, + decodeMessage: (payload: any) => { + return Promise.resolve(payload); + }, + }; + } + + get dataTransferMode(): DataTransferMode { + return this.theDataTransferModeToUse; + } + + get endpointUrl(): string { + return this.theEndpointUrlToUse; + } + + get id(): string { + return this.__instanceId; + } + + get info(): StandardAdapterInfo { + return { + id: 'nlbridge-adapter', + capabilities: { + textChat: true, + audio: false, + fileUpload: false, + }, + inputFormats: ['text'], + outputFormats: ['text', 'markdown'], + }; + } + + async decode(payload: string): Promise { + return undefined; + } + + async encode(message: string): Promise { + return undefined; + } + + abstract fetchText(message: string, extras: AdapterExtras): Promise; + + abstract streamText(message: string, observer: StreamingAdapterObserver, extras: AdapterExtras): void; +} diff --git a/packages/js/nlbridge/src/nlbridge/adapter/fetch.ts b/packages/js/nlbridge/src/nlbridge/adapter/fetch.ts new file mode 100644 index 00000000..130b7ca2 --- /dev/null +++ b/packages/js/nlbridge/src/nlbridge/adapter/fetch.ts @@ -0,0 +1,51 @@ +import {AdapterExtras, NluxError, NluxUsageError, StreamingAdapterObserver} from '@nlux/core'; +import {NlBridgeAbstractAdapter} from './adapter'; + +export class NlBridgeFetchAdapter extends NlBridgeAbstractAdapter { + constructor(options: any) { + super(options); + } + + async fetchText(message: string, extras: AdapterExtras): Promise { + const response = await fetch(this.endpointUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + action: 'chat', + payload: { + message, + }, + }), + }); + + if (!response.ok) { + throw new NluxError({ + source: this.constructor.name, + message: `NlBridge adapter returned status code: ${response.status}`, + }); + } + + const body = await response.json(); + if ( + typeof body === 'object' && body !== null && body.success === true && + typeof body.result === 'object' && body.result !== null && + typeof body.result.response === 'string' + ) { + return body.result.response; + } else { + throw new NluxError({ + source: this.constructor.name, + message: 'Invalid response from NlBridge: String expected.', + }); + } + } + + streamText(message: string, observer: StreamingAdapterObserver, extras: AdapterExtras): void { + throw new NluxUsageError({ + source: this.constructor.name, + message: 'Cannot stream text from the fetch adapter!', + }); + } +} diff --git a/packages/js/nlbridge/src/nlbridge/adapter/stream.ts b/packages/js/nlbridge/src/nlbridge/adapter/stream.ts new file mode 100644 index 00000000..24466227 --- /dev/null +++ b/packages/js/nlbridge/src/nlbridge/adapter/stream.ts @@ -0,0 +1,61 @@ +import {AdapterExtras, NluxUsageError, StreamingAdapterObserver, warn} from '@nlux/core'; +import {NlBridgeAbstractAdapter} from './adapter'; + +export class NlBridgeStreamAdapter extends NlBridgeAbstractAdapter { + constructor(options: any) { + super(options); + } + + async fetchText(message: string, extras: AdapterExtras): Promise { + throw new NluxUsageError({ + source: this.constructor.name, + message: 'Cannot fetch text using the stream adapter!', + }); + } + + streamText(message: string, observer: StreamingAdapterObserver, extras: AdapterExtras): void { + fetch(this.endpointUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + action: 'chat-stream', + payload: { + message, + }, + }), + }).then(async (response) => { + if (!response.ok) { + throw new Error(`NlBridge adapter returned status code: ${response.status}`); + } + + if (!response.body) { + throw new Error(`NlBridge adapter returned status code: ${response.status}`); + } + + // Read a stream of server-sent events + // and feed them to the observer as they are being generated + const reader = response.body.getReader(); + const textDecoder = new TextDecoder(); + let doneReading = false; + + while (!doneReading) { + const {value, done} = await reader.read(); + if (done) { + doneReading = true; + continue; + } + + try { + const chunk = textDecoder.decode(value); + observer.next(chunk); + } catch (err) { + warn(`Error parsing chunk by NlBridgeStreamAdapter: ${err}`); + } + } + + observer.complete(); + }); + } +} diff --git a/packages/js/nlbridge/src/nlbridge/builder/builder.ts b/packages/js/nlbridge/src/nlbridge/builder/builder.ts new file mode 100644 index 00000000..4dabc825 --- /dev/null +++ b/packages/js/nlbridge/src/nlbridge/builder/builder.ts @@ -0,0 +1,7 @@ +import {AdapterBuilder, DataTransferMode, StandardAdapter} from '@nlux/core'; + +export interface NlBridgeAdapterBuilder extends AdapterBuilder { + create(): StandardAdapter; + withDataTransferMode(mode: DataTransferMode): NlBridgeAdapterBuilder; + withUrl(endpointUrl: string): NlBridgeAdapterBuilder; +} diff --git a/packages/js/nlbridge/src/nlbridge/builder/builderImpl.ts b/packages/js/nlbridge/src/nlbridge/builder/builderImpl.ts new file mode 100644 index 00000000..d9041272 --- /dev/null +++ b/packages/js/nlbridge/src/nlbridge/builder/builderImpl.ts @@ -0,0 +1,66 @@ +import {DataTransferMode, NluxUsageError, StandardAdapter} from '@nlux/core'; +import {NlBridgeAbstractAdapter} from '../adapter/adapter'; +import {NlBridgeFetchAdapter} from '../adapter/fetch'; +import {NlBridgeStreamAdapter} from '../adapter/stream'; +import {NlBridgeAdapterOptions} from '../types/adapterOptions'; +import {NlBridgeAdapterBuilder} from './builder'; + +export class NlBridgeAdapterBuilderImpl implements NlBridgeAdapterBuilder { + private theDataTransferMode?: DataTransferMode; + private theUrl?: string; + + constructor(cloneFrom?: NlBridgeAdapterBuilderImpl) { + if (cloneFrom) { + this.theDataTransferMode = cloneFrom.theDataTransferMode; + this.theUrl = cloneFrom.theUrl; + } + } + + create(): StandardAdapter { + if (!this.theUrl) { + throw new NluxUsageError({ + source: this.constructor.name, + message: 'Unable to create NlBridge adapter. URL is missing. ' + + 'Make sure you are calling withUrl() before calling create().', + }); + } + + const options: NlBridgeAdapterOptions = { + url: this.theUrl, + dataTransferMode: this.theDataTransferMode, + }; + + const dataTransferModeToUse = options.dataTransferMode + ?? NlBridgeAbstractAdapter.defaultDataTransferMode; + + if (dataTransferModeToUse === 'stream') { + return new NlBridgeStreamAdapter(options); + } + + return new NlBridgeFetchAdapter(options); + } + + withDataTransferMode(mode: DataTransferMode): NlBridgeAdapterBuilder { + if (this.theDataTransferMode !== undefined) { + throw new NluxUsageError({ + source: this.constructor.name, + message: 'Cannot set the data loading mode more than once', + }); + } + + this.theDataTransferMode = mode; + return this; + } + + withUrl(endpointUrl: string): NlBridgeAdapterBuilder { + if (this.theUrl !== undefined) { + throw new NluxUsageError({ + source: this.constructor.name, + message: 'Cannot set the endpoint URL option more than once', + }); + } + + this.theUrl = endpointUrl; + return this; + } +} diff --git a/packages/js/nlbridge/src/nlbridge/builder/createAdapter.ts b/packages/js/nlbridge/src/nlbridge/builder/createAdapter.ts new file mode 100644 index 00000000..ac172091 --- /dev/null +++ b/packages/js/nlbridge/src/nlbridge/builder/createAdapter.ts @@ -0,0 +1,4 @@ +import {NlBridgeAdapterBuilder} from './builder'; +import {NlBridgeAdapterBuilderImpl} from './builderImpl'; + +export const createAdapter = (): NlBridgeAdapterBuilder => new NlBridgeAdapterBuilderImpl(); diff --git a/packages/js/nlbridge/src/nlbridge/types/adapterOptions.ts b/packages/js/nlbridge/src/nlbridge/types/adapterOptions.ts new file mode 100644 index 00000000..c6d7a045 --- /dev/null +++ b/packages/js/nlbridge/src/nlbridge/types/adapterOptions.ts @@ -0,0 +1,15 @@ +import {DataTransferMode} from '@nlux/core'; + +export type NlBridgeAdapterOptions = { + /** + * The URL of the NlBridge endpoint. + * + */ + url: string; + + /** + * The data transfer mode to use when communicating with NlBridge. + * If not provided, the adapter will use `stream` mode. + */ + dataTransferMode?: DataTransferMode; +}; diff --git a/packages/js/openai/src/openai/gpt/adapters/adapter.ts b/packages/js/openai/src/openai/gpt/adapters/adapter.ts index 4ef96525..26612da9 100644 --- a/packages/js/openai/src/openai/gpt/adapters/adapter.ts +++ b/packages/js/openai/src/openai/gpt/adapters/adapter.ts @@ -4,7 +4,6 @@ import { StandardAdapter, StandardAdapterConfig, StandardAdapterInfo, - StandardAdapterStatus, StreamingAdapterObserver, uid, warn, @@ -19,7 +18,6 @@ export abstract class OpenAiAbstractAdapter imp InboundPayload, OutboundPayload > { - protected currentStatus: StandardAdapterStatus = 'disconnected'; protected readonly model: OpenAIModel; protected readonly openai: OpenAI; protected systemMessage: string | null = 'Act as a helpful assistant to the user'; @@ -28,14 +26,13 @@ export abstract class OpenAiAbstractAdapter imp private readonly __instanceId: string; protected constructor({ - systemMessage, - apiKey, - dataTransferMode, - model, - }: OpenAiAdapterOptions) { + systemMessage, + apiKey, + dataTransferMode, + model, + }: OpenAiAdapterOptions) { this.__instanceId = `${this.info.id}-${uid()}`; - this.currentStatus = 'disconnected'; this.theDataTransferMode = dataTransferMode ?? defaultDataTransferMode; this.model = model ?? defaultChatGptModel; @@ -72,10 +69,6 @@ export abstract class OpenAiAbstractAdapter imp return gptAdapterInfo; } - get status(): StandardAdapterStatus { - return this.currentStatus; - } - async decode(payload: InboundPayload): Promise { const {decodeMessage} = this.config; return decodeMessage(payload); diff --git a/packages/react/langchain/src/index.ts b/packages/react/langchain/src/index.ts index 874b6ba9..72baf9ba 100644 --- a/packages/react/langchain/src/index.ts +++ b/packages/react/langchain/src/index.ts @@ -2,6 +2,7 @@ export type { Adapter, StandardAdapter, DataTransferMode, + StreamingAdapterObserver, LangServeAdapterOptions, LangServeAdapterBuilder, } from '@nlux/langchain'; diff --git a/packages/react/nlbridge/package.template.json b/packages/react/nlbridge/package.template.json new file mode 100644 index 00000000..f6c28114 --- /dev/null +++ b/packages/react/nlbridge/package.template.json @@ -0,0 +1,30 @@ +{ + "name": "@nlux-dev/nlbridge-react", + "version": "0.0.0-latest", + "license": "MPL-2.0", + "scripts": { + "build": "rollup --config rollup.config.ts --configPlugin 'typescript={moduleResolution: \"bundler\"}' --resolveJsonModule", + "watch": "rollup --config rollup.config.ts --configPlugin 'typescript={moduleResolution: \"bundler\"}' --resolveJsonModule --watch" + }, + "dependencies": {}, + "peerDependencies": { + "@nlux/react": "{versions.nlux}", + "react": "^{versions.peerDependencies.react}" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^5.0.5", + "@rollup/plugin-strip": "^3.0.4", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.6", + "rollup": "^4.9.6", + "rollup-plugin-dts": "^6.1.0", + "rollup-plugin-esbuild": "^6.1.1", + "tslib": "^2.6.2" + }, + "main": "index.js", + "types": "nlbridge-react.d.ts", + "module": "esm/nlbridge-react.js", + "browser": "umd/nlbridge-react.js" +} diff --git a/packages/react/nlbridge/rollup.config.ts b/packages/react/nlbridge/rollup.config.ts new file mode 100644 index 00000000..0b5ea1e6 --- /dev/null +++ b/packages/react/nlbridge/rollup.config.ts @@ -0,0 +1,44 @@ +import commonjs from '@rollup/plugin-commonjs'; +import replace from '@rollup/plugin-replace'; +import strip from '@rollup/plugin-strip'; +import terser from '@rollup/plugin-terser'; +import {RollupOptions} from 'rollup'; +import esbuild from 'rollup-plugin-esbuild'; +import {generateDts} from '../../../pipeline/utils/rollup/generateDts'; +import {generateOutputConfig} from '../../../pipeline/utils/rollup/generateOutputConfig'; + +const isProduction = process.env.NODE_ENV === 'production'; +const packageName = '@nlux/nlbridge-react'; +const outputFile = 'nlbridge-react'; + +const packageConfig: () => Promise = async () => ([ + { + input: './src/index.ts', + logLevel: 'silent', + treeshake: 'smallest', + strictDeprecations: true, + plugins: [ + commonjs(), + esbuild(), + isProduction && strip({ + include: '**/*.(mjs|js|ts)', + functions: ['debug', 'console.log', 'console.info'], + }), + replace({ + values: { + 'process.env.NLUX_DEBUG_ENABLED': JSON.stringify(isProduction ? 'false' : 'true'), + }, + preventAssignment: true, + }), + isProduction && terser(), + ], + external: [ + '@nlux/react', + 'react', + ], + output: generateOutputConfig(packageName, outputFile, isProduction), + }, + generateDts(outputFile, isProduction), +]); + +export default packageConfig; diff --git a/packages/react/nlbridge/src/hooks/getAdapterBuilder.ts b/packages/react/nlbridge/src/hooks/getAdapterBuilder.ts new file mode 100644 index 00000000..252be3f3 --- /dev/null +++ b/packages/react/nlbridge/src/hooks/getAdapterBuilder.ts @@ -0,0 +1,26 @@ +import {createAdapter, NlBridgeAdapterBuilder, NlBridgeAdapterOptions} from '@nlux/nlbridge'; + +const source = 'hooks/getAdapterBuilder'; + +export const getAdapterBuilder = (options: NlBridgeAdapterOptions): NlBridgeAdapterBuilder => { + const { + url, + dataTransferMode, + } = options || {}; + + if (dataTransferMode && dataTransferMode !== 'stream' && dataTransferMode !== 'fetch') { + throw new Error(`Data transfer mode not supported`); + } + + if (!url) { + throw new Error(`Runnable URL is required`); + } + + let newAdapter = createAdapter().withUrl(url); + + if (dataTransferMode) { + newAdapter = newAdapter.withDataTransferMode(dataTransferMode); + } + + return newAdapter; +}; diff --git a/packages/react/nlbridge/src/hooks/useAdapter.ts b/packages/react/nlbridge/src/hooks/useAdapter.ts new file mode 100644 index 00000000..16324fae --- /dev/null +++ b/packages/react/nlbridge/src/hooks/useAdapter.ts @@ -0,0 +1,32 @@ +import {NlBridgeAdapterBuilder, NlBridgeAdapterOptions} from '@nlux/nlbridge'; +import {useEffect, useState} from 'react'; +import {getAdapterBuilder} from './getAdapterBuilder'; + +const source = 'hooks/useAdapter'; + +export const useAdapter = (options: NlBridgeAdapterOptions) => { + const [isInitialized, setIsInitialized] = useState(false); + const [adapter, setAdapter] = useState( + getAdapterBuilder(options), + ); + + const { + url, + dataTransferMode, + } = options || {}; + + useEffect(() => { + if (!isInitialized) { + setIsInitialized(true); + return; + } + + const newAdapter = getAdapterBuilder(options); + setAdapter(newAdapter); + }, [ + url, + dataTransferMode, + ]); + + return adapter; +}; diff --git a/packages/react/nlbridge/src/index.ts b/packages/react/nlbridge/src/index.ts new file mode 100644 index 00000000..60008f87 --- /dev/null +++ b/packages/react/nlbridge/src/index.ts @@ -0,0 +1,14 @@ +export type { + Adapter, + StandardAdapter, + DataTransferMode, + StreamingAdapterObserver, + NlBridgeAdapterOptions, + NlBridgeAdapterBuilder, +} from '@nlux/nlbridge'; + +export { + createAdapter, +} from '@nlux/nlbridge'; + +export {useAdapter} from './hooks/useAdapter'; diff --git a/pipeline/npm/core/README.md b/pipeline/npm/core/README.md index f2e531f9..1ec32a3d 100644 --- a/pipeline/npm/core/README.md +++ b/pipeline/npm/core/README.md @@ -88,7 +88,7 @@ from [`@nlux/themes`](https://www.npmjs.com/package/@nlux/themes) or use the CDN hosted version from below: ```jsx - + ``` This CDN is provided for demo purposes only and it's not scalable. diff --git a/pipeline/npm/nlbridge-react/README.md b/pipeline/npm/nlbridge-react/README.md new file mode 100644 index 00000000..20e56a7f --- /dev/null +++ b/pipeline/npm/nlbridge-react/README.md @@ -0,0 +1,24 @@ +# `nlbridge` adapter for `nlux React` + +[![Free And Open Source](https://img.shields.io/badge/Free%20%26%20Open%20Source-%2348c342)](https://github.com/nluxai/nlux) [![Docs nlux.ai](https://img.shields.io/badge/Docs_Website-nlux.dev-%23fa896b)](https://nlux.dev) + +This package enables the integration between `nlux React` and `nlbridge` โ€• the middleware library +for building conversational AI interfaces. + +## About `nlbridge` + +`nlbridge` is a lightweight middleware library that provides a simple and flexible interface to integrate +conversational AI interfaces into your web app or website. It acts as a bridge between your front-end +and the language model API, providing a simple and consistent API to interact with several LLMs. + +`nlbridge` is developed by the same team behind `nlux`, and is designed to work seamlessly with `nlux`. + +### Vanilla JS ๐ŸŸจ vs React JS โš›๏ธ + +This package `@nlux/nlbridge-react` is meant for use with the React JS version of `nlux`. +If you're looking for the Vanilla JS version, please check +the [`@nlux/nlbridge`](https://www.npmjs.com/package/@nlux/nlbridge) package. + +### Docs & Examples ๐Ÿ“– + +For developer documentation, examples, and API reference โ€• you can visit: [nlux.dev](https://nlux.dev/) diff --git a/pipeline/npm/nlbridge-react/index.js b/pipeline/npm/nlbridge-react/index.js new file mode 100644 index 00000000..62b24f94 --- /dev/null +++ b/pipeline/npm/nlbridge-react/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./cjs/nlbridge-react.js'); diff --git a/pipeline/npm/nlbridge-react/package.template.json b/pipeline/npm/nlbridge-react/package.template.json new file mode 100644 index 00000000..9d3b5cbf --- /dev/null +++ b/pipeline/npm/nlbridge-react/package.template.json @@ -0,0 +1,20 @@ +{ + "name": "@nlux/nlbridge-react", + "version": "{versions.nlux}", + "license": "MPL-2.0", + "description": "The nlbridge adapters for nlux React, the React JS library for building conversational AI interfaces.", + "repository": { + "directory": "packages/react/nlbridge" + }, + "dependencies": { + "@nlux/nlbridge": "^{versions.nlux}" + }, + "peerDependencies": { + "@nlux/react": "{versions.nlux}", + "react": "^{versions.peerDependencies.react}" + }, + "main": "index.js", + "types": "nlbridge-react.d.ts", + "module": "esm/nlbridge-react.js", + "browser": "umd/nlbridge-react.js" +} diff --git a/pipeline/npm/nlbridge/README.md b/pipeline/npm/nlbridge/README.md new file mode 100644 index 00000000..1c659158 --- /dev/null +++ b/pipeline/npm/nlbridge/README.md @@ -0,0 +1,24 @@ +# `nlbridge` adapter for `nlux JS` + +[![Free And Open Source](https://img.shields.io/badge/Free%20%26%20Open%20Source-%2348c342)](https://github.com/nluxai/nlux) [![Docs nlux.ai](https://img.shields.io/badge/Docs_Website-nlux.dev-%23fa896b)](https://nlux.dev) + +This package enables the integration between `nlux JS` and `nlbridge` โ€• the middleware library +for building conversational AI interfaces. + +## About `nlbridge` + +`nlbridge` is a lightweight middleware library that provides a simple and flexible interface to integrate +conversational AI interfaces into your web app or website. It acts as a bridge between your front-end +and the language model API, providing a simple and consistent API to interact with several LLMs. + +`nlbridge` is developed by the same team behind `nlux`, and is designed to work seamlessly with `nlux`. + +### Vanilla JS ๐ŸŸจ vs React JS โš›๏ธ + +This package `@nlux/nlbridge` is meant for use with the vanilla JS version of `nlux`. +If you're looking for the React JS version, please check +the [`@nlux/nlbridge-react`](https://www.npmjs.com/package/@nlux/nlbridge-react) package. + +### Docs & Examples ๐Ÿ“– + +For developer documentation, examples, and API reference โ€• you can visit: [nlux.dev](https://nlux.dev/) diff --git a/pipeline/npm/nlbridge/index.js b/pipeline/npm/nlbridge/index.js new file mode 100644 index 00000000..565383b8 --- /dev/null +++ b/pipeline/npm/nlbridge/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./cjs/nlbridge.js'); diff --git a/pipeline/npm/nlbridge/package.template.json b/pipeline/npm/nlbridge/package.template.json new file mode 100644 index 00000000..94a653b6 --- /dev/null +++ b/pipeline/npm/nlbridge/package.template.json @@ -0,0 +1,18 @@ +{ + "name": "@nlux/nlbridge", + "version": "{versions.nlux}", + "license": "MPL-2.0", + "description": "The nlbridge adapters for nlux, the javascript library for building conversational AI interfaces.", + "repository": { + "directory": "packages/js/nlbridge" + }, + "dependencies": { + "@nlux/core": "{versions.nlux}" + }, + "peerDependencies": { + }, + "main": "index.js", + "types": "nlbridge.d.ts", + "module": "esm/nlbridge.js", + "browser": "umd/nlbridge.js" +} diff --git a/pipeline/npm/versions.json b/pipeline/npm/versions.json index bf7ca527..2d45c2eb 100644 --- a/pipeline/npm/versions.json +++ b/pipeline/npm/versions.json @@ -1,6 +1,6 @@ { "inherit": true, - "nlux": "0.11.2", + "nlux": "0.12.0", "peerDependencies": { "react": "18.2.0", "react-dom": "18.2.0" diff --git a/pipeline/scripts/dev.mjs b/pipeline/scripts/dev.mjs index b1559c32..1f2fea63 100644 --- a/pipeline/scripts/dev.mjs +++ b/pipeline/scripts/dev.mjs @@ -51,12 +51,13 @@ try { await Promise.all([ '01-vanilla-js-with-adapters', '02-vanilla-js-with-events', - '03-react-js-with-hugging-face', - '04-react-js-with-langserve', - '05-react-js-with-adapters', - '06-react-js-personas', - '07-react-js-events', - '08-react-js-with-conv-history', + '03-react-js-with-nlbridge', + '04-react-js-with-hugging-face', + '05-react-js-with-langserve', + '06-react-js-with-adapters', + '07-react-js-personas', + '08-react-js-events', + '09-react-js-with-conv-history', ].map(async (name) => { cpSync(`samples/emulator/src/${name}/index.html`, `dist/public/${name}/index.html`); cpSync(`samples/emulator/dep/loaders/require.min.js`, `dist/public/${name}/require.min.js`); diff --git a/pipeline/scripts/packages.mjs b/pipeline/scripts/packages.mjs index 75982157..75c7547a 100644 --- a/pipeline/scripts/packages.mjs +++ b/pipeline/scripts/packages.mjs @@ -30,6 +30,24 @@ export const packages = { npmName: '@nlux/react', npmConfigDirectory: 'pipeline/npm/react', }, + nlbridge: { + name: 'nlbridge', + platform: PackagePlatform.js, + content: PackageContent.adapter, + directory: 'js/nlbridge', + devName: '@nlux-dev/nlbridge', + npmName: '@nlux/nlbridge', + npmConfigDirectory: 'pipeline/npm/nlbridge', + }, + nlbridgeReact: { + name: 'nlbridge-react', + platform: PackagePlatform.react, + content: PackageContent.adapter, + directory: 'react/nlbridge', + devName: '@nlux-dev/nlbridge-react', + npmName: '@nlux/nlbridge-react', + npmConfigDirectory: 'pipeline/npm/nlbridge-react', + }, langchain: { name: 'langchain', platform: PackagePlatform.js, diff --git a/samples/emulator/rollup.config.ts b/samples/emulator/rollup.config.ts index a739283e..aad38b64 100644 --- a/samples/emulator/rollup.config.ts +++ b/samples/emulator/rollup.config.ts @@ -66,7 +66,7 @@ const packageConfig: () => Promise = async () => ([ ], }, // - // 01 - Pure JS in ESM format with Event Listeners + // 02 - Pure JS in ESM format with Event Listeners // { input: './src/02-vanilla-js-with-events/index.ts', @@ -104,10 +104,10 @@ const packageConfig: () => Promise = async () => ([ ], }, // - // 03 - React JS + HF Adapter in UMD format + // 03 - React JS + nlbridge adapter in UMD format // { - input: './src/03-react-js-with-hugging-face/index.tsx', + input: './src/03-react-js-with-nlbridge/index.tsx', plugins: [ esbuild({ jsx: 'transform', @@ -133,7 +133,7 @@ const packageConfig: () => Promise = async () => ([ external: externals, output: [ { - file: `${outputFolder}/03-react-js-with-hugging-face/index.js`, + file: `${outputFolder}/03-react-js-with-nlbridge/index.js`, format: 'umd', sourcemap: true, strict: true, @@ -143,10 +143,10 @@ const packageConfig: () => Promise = async () => ([ ], }, // - // 04 - React JS + LangServe Adapter in UMD format + // 04 - React JS + HF Adapter in UMD format // { - input: './src/04-react-js-with-langserve/index.tsx', + input: './src/04-react-js-with-hugging-face/index.tsx', plugins: [ esbuild({ jsx: 'transform', @@ -172,7 +172,7 @@ const packageConfig: () => Promise = async () => ([ external: externals, output: [ { - file: `${outputFolder}/04-react-js-with-langserve/index.js`, + file: `${outputFolder}/04-react-js-with-hugging-face/index.js`, format: 'umd', sourcemap: true, strict: true, @@ -182,10 +182,10 @@ const packageConfig: () => Promise = async () => ([ ], }, // - // 05 - React JS in UMD format + Custom Adapters + // 05 - React JS + LangServe Adapter in UMD format // { - input: './src/05-react-js-with-adapters/index.tsx', + input: './src/05-react-js-with-langserve/index.tsx', plugins: [ esbuild({ jsx: 'transform', @@ -211,7 +211,7 @@ const packageConfig: () => Promise = async () => ([ external: externals, output: [ { - file: `${outputFolder}/05-react-js-with-adapters/index.js`, + file: `${outputFolder}/05-react-js-with-langserve/index.js`, format: 'umd', sourcemap: true, strict: true, @@ -221,10 +221,10 @@ const packageConfig: () => Promise = async () => ([ ], }, // - // 06 - React JS + OpenAI in UMD format + Persona Demo + // 06 - React JS in UMD format + Custom Adapters // { - input: './src/06-react-js-personas/index.tsx', + input: './src/06-react-js-with-adapters/index.tsx', plugins: [ esbuild({ jsx: 'transform', @@ -250,7 +250,7 @@ const packageConfig: () => Promise = async () => ([ external: externals, output: [ { - file: `${outputFolder}/06-react-js-personas/index.js`, + file: `${outputFolder}/06-react-js-with-adapters/index.js`, format: 'umd', sourcemap: true, strict: true, @@ -260,10 +260,10 @@ const packageConfig: () => Promise = async () => ([ ], }, // - // 07 - React JS + OpenAI in UMD format + Event Listeners + // 07 - React JS + OpenAI in UMD format + Persona Demo // { - input: './src/07-react-js-events/index.tsx', + input: './src/07-react-js-personas/index.tsx', plugins: [ esbuild({ jsx: 'transform', @@ -289,7 +289,7 @@ const packageConfig: () => Promise = async () => ([ external: externals, output: [ { - file: `${outputFolder}/07-react-js-events/index.js`, + file: `${outputFolder}/07-react-js-personas/index.js`, format: 'umd', sourcemap: true, strict: true, @@ -299,10 +299,10 @@ const packageConfig: () => Promise = async () => ([ ], }, // - // 08 - React JS + Conversation History + // 08 - React JS + OpenAI in UMD format + Event Listeners // { - input: './src/08-react-js-with-conv-history/index.tsx', + input: './src/08-react-js-events/index.tsx', plugins: [ esbuild({ jsx: 'transform', @@ -328,7 +328,46 @@ const packageConfig: () => Promise = async () => ([ external: externals, output: [ { - file: `${outputFolder}/08-react-js-with-conv-history/index.js`, + file: `${outputFolder}/08-react-js-events/index.js`, + format: 'umd', + sourcemap: true, + strict: true, + esModule: false, + name: 'nluxEmulatorReactExample', + }, + ], + }, + // + // 09 - React JS + Conversation History + // + { + input: './src/09-react-js-with-conv-history/index.tsx', + plugins: [ + esbuild({ + jsx: 'transform', + jsxFactory: 'React.createElement', + jsxFragment: 'React.Fragment', + }), + nodeResolve({ + modulePaths: [ + nodeModulesPath, + ], + rootDir: '/packages', + browser: true, + }), + commonjs(), + replace({ + delimiters: ['', ''], + preventAssignment: false, + values: { + 'process.env.NODE_ENV': JSON.stringify('development'), + }, + }), + ], + external: externals, + output: [ + { + file: `${outputFolder}/09-react-js-with-conv-history/index.js`, format: 'umd', sourcemap: true, strict: true, diff --git a/samples/emulator/src/01-vanilla-js-with-adapters/index.ts b/samples/emulator/src/01-vanilla-js-with-adapters/index.ts index b3425afd..6ce8e1aa 100644 --- a/samples/emulator/src/01-vanilla-js-with-adapters/index.ts +++ b/samples/emulator/src/01-vanilla-js-with-adapters/index.ts @@ -2,6 +2,7 @@ import {AiChat, createAiChat} from '@nlux/core'; import {highlighter} from '@nlux/highlighter'; import {createAdapter as createLangServeAdapter} from '@nlux/langchain'; import {createAdapter as createOpenAiAdapter} from '@nlux/openai'; +import {nlBridgeCustomPromiseAdapter} from './nlBridgeCustomAdapter'; import {personaOptions} from './personaOptions'; debugger; @@ -57,9 +58,10 @@ document.addEventListener('DOMContentLoaded', () => { aiChat = createAiChat() // .withAdapter(langServeAdapter) - .withAdapter(openAiAdapter) + // .withAdapter(openAiAdapter) // .withAdapter(myCustomStreamingAdapter) // .withAdapter(myCustomPromiseAdapter) + .withAdapter(nlBridgeCustomPromiseAdapter) .withSyntaxHighlighter(highlighter) .withConversationOptions({ historyPayloadSize: 3, diff --git a/samples/emulator/src/01-vanilla-js-with-adapters/nlBridgeCustomAdapter.ts b/samples/emulator/src/01-vanilla-js-with-adapters/nlBridgeCustomAdapter.ts new file mode 100644 index 00000000..3b347539 --- /dev/null +++ b/samples/emulator/src/01-vanilla-js-with-adapters/nlBridgeCustomAdapter.ts @@ -0,0 +1,88 @@ +import {Adapter, AdapterExtras, StreamingAdapterObserver} from '@nlux/core'; + +export const nlBridgeCustomStreamingAdapter: Adapter = { + streamText: ( + message: string, + observer: StreamingAdapterObserver, + extras: AdapterExtras, + ) => { + const endpoint = 'http://localhost:8899/'; + fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + action: 'chat-stream', + payload: { + message, + }, + }), + }).then(async (response) => { + if (!response.ok) { + throw new Error(`NlBridge adapter returned status code: ${response.status}`); + } + + if (!response.body) { + throw new Error(`NlBridge adapter returned status code: ${response.status}`); + } + + // Read a stream of server-sent events + // and feed them to the observer as they are being generated + const reader = response.body.getReader(); + const textDecoder = new TextDecoder(); + let doneReading = false; + + while (!doneReading) { + const {value, done} = await reader.read(); + if (done) { + doneReading = true; + continue; + } + + try { + const chunk = textDecoder.decode(value); + observer.next(chunk); + } catch (error) { + console.warn('Error parsing chunk', error); + } + } + + observer.complete(); + }); + }, +}; + +export const nlBridgeCustomPromiseAdapter: Adapter = { + async fetchText(message: string, extras): Promise { + const endpoint = 'http://localhost:8899/'; + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + action: 'chat', + payload: { + message, + }, + }), + }); + + if (!response.ok) { + throw new Error(`NlBridge adapter returned status code: ${response.status}`); + } + + const body = await response.json(); + if ( + typeof body === 'object' && body !== null && body.success === true && + typeof body.result === 'object' && body.result !== null && + typeof body.result.response === 'string' + ) { + return body.result.response; + } else { + throw new Error('NlBridge adapter returned invalid response'); + } + }, +}; + diff --git a/samples/emulator/src/03-react-js-with-nlbridge/index.html b/samples/emulator/src/03-react-js-with-nlbridge/index.html new file mode 100644 index 00000000..b389276f --- /dev/null +++ b/samples/emulator/src/03-react-js-with-nlbridge/index.html @@ -0,0 +1,63 @@ + + + + + + + nlux React Demo! + + + + + + + + + + + +
+ + + diff --git a/samples/emulator/src/03-react-js-with-nlbridge/index.tsx b/samples/emulator/src/03-react-js-with-nlbridge/index.tsx new file mode 100644 index 00000000..f471e417 --- /dev/null +++ b/samples/emulator/src/03-react-js-with-nlbridge/index.tsx @@ -0,0 +1,58 @@ +import {useAdapter} from '@nlux/nlbridge-react'; +import {AiChat} from '@nlux/react'; +import React, {useCallback, useState} from 'react'; +import {createRoot} from 'react-dom/client'; + +const ExampleWrapper = () => { + const [maxHeight, setMaxHeight] = useState(550); + const [key, setKey] = useState(0); + + const handleRandomContainerHeight = useCallback(() => { + const newHeight = Math.floor(Math.random() * 1000); + setMaxHeight(newHeight); + }, []); + + const nlBridge = useAdapter({ + url: 'http://localhost:8899/', + dataTransferMode: 'stream', + }); + + return ( +
+ {key} + + +
+ +
+
+ ); +}; + +export default () => { + const root = document.getElementById('nlux-ai-chat-root'); + if (!root) { + throw new Error('Root element not found'); + } + + const reactRoot = createRoot(root); + reactRoot.render( + + + , + ); +}; diff --git a/samples/emulator/src/03-react-js-with-hugging-face/index.html b/samples/emulator/src/04-react-js-with-hugging-face/index.html similarity index 100% rename from samples/emulator/src/03-react-js-with-hugging-face/index.html rename to samples/emulator/src/04-react-js-with-hugging-face/index.html diff --git a/samples/emulator/src/03-react-js-with-hugging-face/index.tsx b/samples/emulator/src/04-react-js-with-hugging-face/index.tsx similarity index 100% rename from samples/emulator/src/03-react-js-with-hugging-face/index.tsx rename to samples/emulator/src/04-react-js-with-hugging-face/index.tsx diff --git a/samples/emulator/src/04-react-js-with-langserve/adapters/einbotFetch.ts b/samples/emulator/src/05-react-js-with-langserve/adapters/einbotFetch.ts similarity index 100% rename from samples/emulator/src/04-react-js-with-langserve/adapters/einbotFetch.ts rename to samples/emulator/src/05-react-js-with-langserve/adapters/einbotFetch.ts diff --git a/samples/emulator/src/04-react-js-with-langserve/adapters/einbotStream.ts b/samples/emulator/src/05-react-js-with-langserve/adapters/einbotStream.ts similarity index 100% rename from samples/emulator/src/04-react-js-with-langserve/adapters/einbotStream.ts rename to samples/emulator/src/05-react-js-with-langserve/adapters/einbotStream.ts diff --git a/samples/emulator/src/04-react-js-with-langserve/adapters/parseChunk.ts b/samples/emulator/src/05-react-js-with-langserve/adapters/parseChunk.ts similarity index 100% rename from samples/emulator/src/04-react-js-with-langserve/adapters/parseChunk.ts rename to samples/emulator/src/05-react-js-with-langserve/adapters/parseChunk.ts diff --git a/samples/emulator/src/04-react-js-with-langserve/index.html b/samples/emulator/src/05-react-js-with-langserve/index.html similarity index 100% rename from samples/emulator/src/04-react-js-with-langserve/index.html rename to samples/emulator/src/05-react-js-with-langserve/index.html diff --git a/samples/emulator/src/04-react-js-with-langserve/index.tsx b/samples/emulator/src/05-react-js-with-langserve/index.tsx similarity index 100% rename from samples/emulator/src/04-react-js-with-langserve/index.tsx rename to samples/emulator/src/05-react-js-with-langserve/index.tsx diff --git a/samples/emulator/src/05-react-js-with-adapters/customAdapter.ts b/samples/emulator/src/06-react-js-with-adapters/customAdapter.ts similarity index 100% rename from samples/emulator/src/05-react-js-with-adapters/customAdapter.ts rename to samples/emulator/src/06-react-js-with-adapters/customAdapter.ts diff --git a/samples/emulator/src/05-react-js-with-adapters/index.html b/samples/emulator/src/06-react-js-with-adapters/index.html similarity index 100% rename from samples/emulator/src/05-react-js-with-adapters/index.html rename to samples/emulator/src/06-react-js-with-adapters/index.html diff --git a/samples/emulator/src/05-react-js-with-adapters/index.tsx b/samples/emulator/src/06-react-js-with-adapters/index.tsx similarity index 100% rename from samples/emulator/src/05-react-js-with-adapters/index.tsx rename to samples/emulator/src/06-react-js-with-adapters/index.tsx diff --git a/samples/emulator/src/05-react-js-with-adapters/personaOptions.tsx b/samples/emulator/src/06-react-js-with-adapters/personaOptions.tsx similarity index 100% rename from samples/emulator/src/05-react-js-with-adapters/personaOptions.tsx rename to samples/emulator/src/06-react-js-with-adapters/personaOptions.tsx diff --git a/samples/emulator/src/05-react-js-with-adapters/stream.ts b/samples/emulator/src/06-react-js-with-adapters/stream.ts similarity index 100% rename from samples/emulator/src/05-react-js-with-adapters/stream.ts rename to samples/emulator/src/06-react-js-with-adapters/stream.ts diff --git a/samples/emulator/src/06-react-js-personas/customAdapter.ts b/samples/emulator/src/07-react-js-personas/customAdapter.ts similarity index 100% rename from samples/emulator/src/06-react-js-personas/customAdapter.ts rename to samples/emulator/src/07-react-js-personas/customAdapter.ts diff --git a/samples/emulator/src/06-react-js-personas/funny-bot.jpeg b/samples/emulator/src/07-react-js-personas/funny-bot.jpeg similarity index 100% rename from samples/emulator/src/06-react-js-personas/funny-bot.jpeg rename to samples/emulator/src/07-react-js-personas/funny-bot.jpeg diff --git a/samples/emulator/src/06-react-js-personas/index.html b/samples/emulator/src/07-react-js-personas/index.html similarity index 100% rename from samples/emulator/src/06-react-js-personas/index.html rename to samples/emulator/src/07-react-js-personas/index.html diff --git a/samples/emulator/src/06-react-js-personas/index.tsx b/samples/emulator/src/07-react-js-personas/index.tsx similarity index 100% rename from samples/emulator/src/06-react-js-personas/index.tsx rename to samples/emulator/src/07-react-js-personas/index.tsx diff --git a/samples/emulator/src/07-react-js-events/index.html b/samples/emulator/src/08-react-js-events/index.html similarity index 100% rename from samples/emulator/src/07-react-js-events/index.html rename to samples/emulator/src/08-react-js-events/index.html diff --git a/samples/emulator/src/07-react-js-events/index.tsx b/samples/emulator/src/08-react-js-events/index.tsx similarity index 100% rename from samples/emulator/src/07-react-js-events/index.tsx rename to samples/emulator/src/08-react-js-events/index.tsx diff --git a/samples/emulator/src/07-react-js-events/stream.ts b/samples/emulator/src/08-react-js-events/stream.ts similarity index 100% rename from samples/emulator/src/07-react-js-events/stream.ts rename to samples/emulator/src/08-react-js-events/stream.ts diff --git a/samples/emulator/src/08-react-js-with-conv-history/index.html b/samples/emulator/src/09-react-js-with-conv-history/index.html similarity index 100% rename from samples/emulator/src/08-react-js-with-conv-history/index.html rename to samples/emulator/src/09-react-js-with-conv-history/index.html diff --git a/samples/emulator/src/08-react-js-with-conv-history/index.tsx b/samples/emulator/src/09-react-js-with-conv-history/index.tsx similarity index 100% rename from samples/emulator/src/08-react-js-with-conv-history/index.tsx rename to samples/emulator/src/09-react-js-with-conv-history/index.tsx diff --git a/samples/emulator/src/index.html b/samples/emulator/src/index.html index c642d611..c6499769 100644 --- a/samples/emulator/src/index.html +++ b/samples/emulator/src/index.html @@ -37,12 +37,13 @@

nlux Emulator Samples

nlux Vanilla JS (ESM Only) With OpenAI Or Custom Adapters nlux Vanilla JS (ESM Only) With Event Listeners - nlux React JS (UMD = ESM + CJS) With Hugging Face Adapter - nlux React JS (UMD) With LangServe Adapters - nlux React JS (UMD) With OpenAI Or Custom Adapters - nlux React JS (UMD) Showcasing Personas Feature - nlux React JS (UMD) With Event Listeners - nlux React JS (UMD) With Conversation History + nlux React JS (UMD = ESM + CJS) With nlbridge Adapter + nlux React JS (UMD = ESM + CJS) With Hugging Face Adapter + nlux React JS (UMD) With LangServe Adapters + nlux React JS (UMD) With OpenAI Or Custom Adapters + nlux React JS (UMD) Showcasing Personas Feature + nlux React JS (UMD) With Event Listeners + nlux React JS (UMD) With Conversation History