diff --git a/examples/agents/ask-my-text-documents-agent/.env.baseai.example b/examples/agents/ask-my-text-documents-agent/.env.baseai.example
new file mode 100644
index 00000000..8c643651
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/.env.baseai.example
@@ -0,0 +1,21 @@
+# !! SERVER SIDE ONLY !!
+# Keep all your API keys secret — use only on the server side.
+
+# TODO: ADD: Both in your production and local env files.
+# Langbase API key for your User or Org account.
+# How to get this API key https://langbase.com/docs/api-reference/api-keys
+LANGBASE_API_KEY=
+
+# TODO: ADD: LOCAL ONLY. Add only to local env files.
+# Following keys are needed for local pipe runs. For providers you are using.
+# For Langbase, please add the key to your LLM keysets.
+# Read more: Langbase LLM Keysets https://langbase.com/docs/features/keysets
+OPENAI_API_KEY=
+ANTHROPIC_API_KEY=
+COHERE_API_KEY=
+FIREWORKS_API_KEY=
+GOOGLE_API_KEY=
+GROQ_API_KEY=
+MISTRAL_API_KEY=
+PERPLEXITY_API_KEY=
+TOGETHER_API_KEY=
diff --git a/examples/agents/ask-my-text-documents-agent/.gitignore b/examples/agents/ask-my-text-documents-agent/.gitignore
new file mode 100644
index 00000000..13a2e600
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/.gitignore
@@ -0,0 +1,58 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env
+.env*.local
+.copy.local.env
+.copy.remote.env
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+# Supabase
+seed.sql
+xseed.sql
+xxseed.sql
+-seed.sql
+/supabase/seed.sql
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
+
+# No lock files.
+package-lock.json
+yarn.lock
+dist
+# baseai
+**/.baseai/
+# env file
+.env
diff --git a/examples/agents/ask-my-text-documents-agent/README.md b/examples/agents/ask-my-text-documents-agent/README.md
new file mode 100644
index 00000000..2a470ecd
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/README.md
@@ -0,0 +1,53 @@
+![Ask My Text Documents Agent by ⌘ Langbase][cover]
+
+![License: MIT][mit] [![Fork on ⌘ Langbase][fork]][pipe]
+
+## Build an Ask My Text Documents Agent with BaseAI framework — ⌘ Langbase
+
+This AI Agent is built using the BaseAI framework. It leverages an agentic pipe that integrates over 30+ LLMs (including OpenAI, Gemini, Mistral, Llama, Gemma, etc.) and can handle any data, with context sizes of up to 10M+ tokens, supported by memory. The framework is compatible with any front-end framework (such as React, Remix, Astro, Next.js), giving you, as a developer, the freedom to tailor your AI application exactly as you envision.
+
+## Features
+
+- Ask My Text Documents Agent — Built with [BaseAI framework and agentic Pipe ⌘ ][qs].
+- Composable Agents — build and compose agents with BaseAI.
+- Add and Sync deployed pipe on Langbase locally npx baseai@latest add ([see the Code button][pipe]).
+
+## Learn more
+
+1. Check the [Learning path to build an agentic AI pipe with ⌘ BaseAI][learn]
+2. Read the [source code on GitHub][gh] for this agent example
+3. Go through Documentaion: [Pipe Quick Start][qs]
+4. Learn more about [Memory features in ⌘ BaseAI][memory]
+5. Learn more about [Tool calls support in ⌘ BaseAI][toolcalls]
+
+
+> NOTE:
+> This is a BaseAI project, you can deploy BaseAI pipes, memory and tool calls on Langbase.
+
+---
+
+## Authors
+
+This project is created by [Langbase][lb] team members, with contributions from:
+
+- Muhammad-Ali Danish - Software Engineer, [Langbase][lb]
+**_Built by ⌘ [Langbase.com][lb] — Ship hyper-personalized AI assistants with memory!_**
+
+
+[lb]: https://langbase.com
+[pipe]: https://langbase.com/examples/ask-my-text-documents-agent
+[gh]: https://github.com/LangbaseInc/baseai/tree/main/examples/agents/ask-my-text-documents-agent
+[cover]:https://raw.githubusercontent.com/LangbaseInc/docs-images/main/baseai/baseai-cover.png
+[download]:https://download-directory.github.io/?url=https://github.com/LangbaseInc/baseai/tree/main/examples/agents/ask-my-text-documents-agent
+[learn]:https://baseai.dev/learn
+[memory]:https://baseai.dev/docs/memory/quickstart
+[toolcalls]:https://baseai.dev/docs/tools/quickstart
+[deploy]:https://baseai.dev/docs/deployment/authentication
+[signup]: https://langbase.fyi/io
+[qs]:https://baseai.dev/docs/pipe/quickstart
+[docs]:https://baseai.dev/docs
+[xaa]:https://x.com/MrAhmadAwais
+[xab]:https://x.com/AhmadBilalDev
+[local]:http://localhost:9000
+[mit]: https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge&color=%23000000
+[fork]: https://img.shields.io/badge/FORK%20ON-%E2%8C%98%20Langbase-000000.svg?style=for-the-badge&logo=%E2%8C%98%20Langbase&logoColor=000000
\ No newline at end of file
diff --git a/examples/agents/ask-my-text-documents-agent/app/api/chat/route.ts b/examples/agents/ask-my-text-documents-agent/app/api/chat/route.ts
new file mode 100644
index 00000000..b527a2a8
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/app/api/chat/route.ts
@@ -0,0 +1,23 @@
+'use server'
+
+import {NextRequest} from 'next/server';
+import { Pipe } from '@baseai/core';
+import { RunOptionsStream } from '@baseai/core';
+import pipeAskMyTextDocumentsAgent from '@/baseai/pipes/ask-my-text-documents-agent';
+
+export async function POST(req: NextRequest) {
+ const runOptions = await req.json() as RunOptionsStream;;
+
+ // 1. Initiate the Pipe.
+ const pipe = new Pipe(pipeAskMyTextDocumentsAgent());
+
+ // 2. Run the Pipe.
+ const {stream, threadId} = await pipe.run(runOptions);
+ // 3. Return the ReadableStream directly.
+ return new Response(stream, {
+ status: 200,
+ headers: {
+ 'lb-thread-id': threadId ?? '',
+ },
+ });
+}
\ No newline at end of file
diff --git a/examples/agents/ask-my-text-documents-agent/app/favicon.ico b/examples/agents/ask-my-text-documents-agent/app/favicon.ico
new file mode 100644
index 00000000..7c6d4a8a
Binary files /dev/null and b/examples/agents/ask-my-text-documents-agent/app/favicon.ico differ
diff --git a/examples/agents/ask-my-text-documents-agent/app/globals.css b/examples/agents/ask-my-text-documents-agent/app/globals.css
new file mode 100755
index 00000000..36084b74
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/app/globals.css
@@ -0,0 +1,99 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+/* Zinc */
+/* --background: 240 10% 3.9%; */
+/* --muted: 240 3.7% 15.9%; */
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 240 10% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 240 10% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 240 10% 3.9%;
+ --primary: 240 5.9% 10%;
+ --primary-foreground: 0 0% 98%;
+ --secondary: 240 4.8% 95.9%;
+ --secondary-foreground: 240 5.9% 10%;
+ --muted: 240 4.8% 95.9%;
+ --muted-foreground: 240 3.8% 46.1%;
+ --accent: 240 4.8% 95.9%;
+ --accent-foreground: 240 5.9% 10%;
+ /* --destructive: 0 84.2% 60.2%; */
+ --destructive: 2.74 92.59% 62.94%;
+ --destructive-foreground: 0 0% 98%;
+ --warning: 46.38 70.61% 48.04%;
+ --warning-foreground: 120 12.5% 3.14%;
+ --border: 240 5.9% 90%;
+ --input: 240 5.9% 90%;
+ --ring: 240 5.9% 10%;
+ --radius: 6px;
+ --danger: 2.74 92.59% 62.94%;
+ }
+
+ .dark {
+ /* --background: 120 12.5% 3.14%; */
+ --background: 240, 3%, 9%;
+ --foreground: 0 0% 98%;
+ --card: 240 10% 3.9%;
+ --card-foreground: 0 0% 98%;
+ --popover: 240 10% 3.9%;
+ --popover-foreground: 0 0% 98%;
+ --primary: 0 0% 98%;
+ --primary-foreground: 240 5.9% 10%;
+ --secondary: 240 3.7% 15.9%;
+ --secondary-foreground: 0 0% 98%;
+ /* --muted: 165 10% 7.84%; */
+ --muted: 240 3.45% 11.37%;
+ --muted-foreground: 240 5% 64.9%;
+ --accent: 240 3.7% 15.9%;
+ --accent-foreground: 0 0% 98%;
+ /* --destructive: 0 62.8% 30.6%; */
+ --destructive: 356.18 70.61% 48.04%;
+ --destructive-foreground: 0 0% 98%;
+ --warning: 46.38 70.61% 48.04%;
+ --warning-foreground: 120 12.5% 3.14%;
+ /* --border: 240 3.7% 15.9%; */
+ --border: 240 2% 14%;
+ --border-muted: 240 2% 14%;
+ --input: 240 3.7% 15.9%;
+ --ring: 240 4.9% 83.9%;
+ --danger: 356.18 70.61% 48.04%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+::selection {
+ color: hsl(var(--background));
+ background: hsl(var(--foreground));
+}
+
+.google {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ position: relative;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: 50%;
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 48 48'%3E%3Cdefs%3E%3Cpath id='a' d='M44.5 20H24v8.5h11.8C34.7 33.9 30.1 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4z'/%3E%3C/defs%3E%3CclipPath id='b'%3E%3Cuse xlink:href='%23a' overflow='visible'/%3E%3C/clipPath%3E%3Cpath clip-path='url(%23b)' fill='%23FBBC05' d='M0 37V11l17 13z'/%3E%3Cpath clip-path='url(%23b)' fill='%23EA4335' d='M0 11l17 13 7-6.1L48 14V0H0z'/%3E%3Cpath clip-path='url(%23b)' fill='%2334A853' d='M0 37l30-23 7.9 1L48 0v48H0z'/%3E%3Cpath clip-path='url(%23b)' fill='%234285F4' d='M48 48L17 24l-4-3 35-10z'/%3E%3C/svg%3E");
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/examples/agents/ask-my-text-documents-agent/app/layout.tsx b/examples/agents/ask-my-text-documents-agent/app/layout.tsx
new file mode 100755
index 00000000..14bdfbf6
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/app/layout.tsx
@@ -0,0 +1,36 @@
+import { Header } from '@/components/header'
+import cn from 'mxcn'
+import type { Metadata } from 'next'
+import { Inter } from 'next/font/google'
+import { Toaster } from 'sonner'
+import './globals.css'
+
+const inter = Inter({ subsets: ['latin'] })
+
+export const metadata: Metadata = {
+ title: 'Ask My Docs RAG',
+ description: 'Ask any query about my docs. Built using Langbase.',
+ keywords: ['AI', 'Chatbot', 'RAG', 'Docs', 'Langbase']
+}
+
+export default function RootLayout({
+ children
+}: Readonly<{
+ children: React.ReactNode
+}>) {
+ return (
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/app/not-found.tsx b/examples/agents/ask-my-text-documents-agent/app/not-found.tsx
new file mode 100644
index 00000000..3409889a
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/app/not-found.tsx
@@ -0,0 +1,58 @@
+export const runtime = "edge";
+
+export default function NotFound() {
+ return (
+ <>
+ 404: This page could not be found.
+
+
+
+
+ 404
+
+
+
This page could not be found.
+
+
+
+ >
+ );
+}
+
+const styles = {
+ error: {
+ fontFamily:
+ 'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',
+ height: "100vh",
+ textAlign: "center",
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ justifyContent: "center",
+ },
+
+ desc: {
+ display: "inline-block",
+ },
+
+ h1: {
+ display: "inline-block",
+ margin: "0 20px 0 0",
+ padding: "0 23px 0 0",
+ fontSize: 24,
+ fontWeight: 500,
+ verticalAlign: "top",
+ lineHeight: "49px",
+ },
+
+ h2: {
+ fontSize: 14,
+ fontWeight: 400,
+ lineHeight: "49px",
+ margin: 0,
+ },
+} as const;
diff --git a/examples/agents/ask-my-text-documents-agent/app/page.tsx b/examples/agents/ask-my-text-documents-agent/app/page.tsx
new file mode 100755
index 00000000..f278d230
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/app/page.tsx
@@ -0,0 +1,7 @@
+import { Chatbot } from '@/components/chatbot-page'
+
+export const runtime = 'edge'
+
+export default function ChatPage() {
+ return
+}
diff --git a/examples/agents/ask-my-text-documents-agent/baseai/baseai.config.ts b/examples/agents/ask-my-text-documents-agent/baseai/baseai.config.ts
new file mode 100644
index 00000000..f0ee748f
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/baseai/baseai.config.ts
@@ -0,0 +1,18 @@
+import type { BaseAIConfig } from 'baseai';
+
+export const config: BaseAIConfig = {
+ log: {
+ isEnabled: true,
+ logSensitiveData: false,
+ pipe: true,
+ 'pipe.completion': true,
+ 'pipe.request': true,
+ 'pipe.response': true,
+ tool: true,
+ memory: true
+ },
+ memory: {
+ useLocalEmbeddings: false
+ },
+ envFilePath: '.env'
+};
diff --git a/examples/agents/ask-my-text-documents-agent/baseai/memory/ask-my-text-documents/documents/Artificial_intelligence_in_the_construction_industry.pdf b/examples/agents/ask-my-text-documents-agent/baseai/memory/ask-my-text-documents/documents/Artificial_intelligence_in_the_construction_industry.pdf
new file mode 100644
index 00000000..99800a1b
Binary files /dev/null and b/examples/agents/ask-my-text-documents-agent/baseai/memory/ask-my-text-documents/documents/Artificial_intelligence_in_the_construction_industry.pdf differ
diff --git a/examples/agents/ask-my-text-documents-agent/baseai/memory/ask-my-text-documents/index.ts b/examples/agents/ask-my-text-documents-agent/baseai/memory/ask-my-text-documents/index.ts
new file mode 100644
index 00000000..e72fc150
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/baseai/memory/ask-my-text-documents/index.ts
@@ -0,0 +1,14 @@
+import { MemoryI } from '@baseai/core';
+import path from 'path';
+
+const memoryAskMyTextDocuments = (): MemoryI => ({
+ name: 'ask-my-text-documents',
+ description: 'text documents as pdf',
+ config: {
+ useGitRepo: false,
+ dirToTrack: path.posix.join('.'),
+ extToTrack: ["*"]
+ }
+});
+
+export default memoryAskMyTextDocuments;
diff --git a/examples/agents/ask-my-text-documents-agent/baseai/pipes/ask-my-text-documents-agent.ts b/examples/agents/ask-my-text-documents-agent/baseai/pipes/ask-my-text-documents-agent.ts
new file mode 100644
index 00000000..78ef4415
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/baseai/pipes/ask-my-text-documents-agent.ts
@@ -0,0 +1,43 @@
+import { PipeI } from '@baseai/core';
+import memoryAskMyTextDocuments from '../memory/ask-my-text-documents';
+
+const pipeAskMyTextDocumentsAgent = (): PipeI => ({
+ // Replace with your API key https://langbase.com/docs/api-reference/api-keys
+ apiKey: process.env.LANGBASE_API_KEY!,
+ name: `ask-my-text-documents-agent`,
+ description: ``,
+ status: `private`,
+ model: `openai:gpt-4o-mini`,
+ stream: true,
+ json: false,
+ store: true,
+ moderate: true,
+ top_p: 1,
+ max_tokens: 1000,
+ temperature: 0.7,
+ presence_penalty: 0,
+ frequency_penalty: 0,
+ stop: [],
+ tool_choice: 'auto',
+ parallel_tool_calls: true,
+ messages: [
+ {
+ role: 'system',
+ content:
+ "You're a helpful AI assistant. Answer user queries in polite manner. If user ask about your purpose you can reply them that your doc agent and they can upload their document and then you will allow them to ask any question about their docs."
+ },
+ { name: 'json', role: 'system', content: '' },
+ { name: 'safety', role: 'system', content: '' },
+ {
+ name: 'opening',
+ role: 'system',
+ content: 'Welcome to Langbase. Prompt away!'
+ },
+ { name: 'rag', role: 'system', content: "Below is some CONTEXT for you to answer the questions. ONLY answer from the CONTEXT. CONTEXT consists of multiple information chunks. Each chunk has a source mentioned at the end.\n\nFor each piece of response you provide, cite the source in brackets like so: [1].\n\nAt the end of the answer, always list each source with its corresponding number and provide the document name. like so [1] Filename.doc.\n\nIf you don't know the answer, just say that you don't know. Ask for more context and better questions if needed." }
+ ],
+ variables: [],
+ tools: [],
+ memory: [memoryAskMyTextDocuments()]
+});
+
+export default pipeAskMyTextDocumentsAgent;
diff --git a/examples/agents/ask-my-text-documents-agent/components/chat-input.tsx b/examples/agents/ask-my-text-documents-agent/components/chat-input.tsx
new file mode 100755
index 00000000..866fac3a
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/chat-input.tsx
@@ -0,0 +1,69 @@
+import { Message } from '@baseai/core/pipes'
+import { PromptForm } from '@/components/prompt-form'
+import { Button } from '@/components/ui/button'
+import { IconRegenerate, IconStop } from '@/components/ui/icons'
+
+export interface ChatInputProps {
+ id?: string;
+ isLoading: boolean;
+ stop: () => void;
+ append: (content: string) => Promise;
+ reload: () => Promise;
+ messages: Message[];
+ input: string;
+ setInput: (value: string) => void;
+}
+
+
+export function ChatInput({
+ id,
+ isLoading,
+ stop,
+ append,
+ reload,
+ input,
+ setInput,
+ messages
+}: ChatInputProps) {
+ return (
+
+
+
+ {isLoading ? (
+ stop()}
+ className="bg-background"
+ size={'sm'}
+ >
+
+ Stop generating
+
+ ) : (
+ messages?.length > 0 && (
+ reload()}
+ className="bg-background"
+ size={'sm'}
+ >
+
+ Regenerate response
+
+ )
+ )}
+
+
+
{
+ await append(value)
+ }}
+ input={input}
+ handleInputChange={setInput}
+ isLoading={isLoading}
+ />
+
+
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/chat-list.tsx b/examples/agents/ask-my-text-documents-agent/components/chat-list.tsx
new file mode 100755
index 00000000..194f5f42
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/chat-list.tsx
@@ -0,0 +1,27 @@
+import { Message } from '@baseai/core/pipes'
+
+import { Separator } from '@/components/ui/separator'
+import { ChatMessage } from '@/components/chat-message'
+
+export interface ChatList {
+ messages: Message[]
+}
+
+export function ChatList({ messages }: ChatList) {
+ if (!messages.length) {
+ return null
+ }
+
+ return (
+
+ {messages.map((message, index) => (
+
+
+ {index < messages.length - 1 && (
+
+ )}
+
+ ))}
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/chat-message-actions.tsx b/examples/agents/ask-my-text-documents-agent/components/chat-message-actions.tsx
new file mode 100755
index 00000000..f63aede1
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/chat-message-actions.tsx
@@ -0,0 +1,40 @@
+'use client'
+
+import { Message } from '@baseai/core/pipes'
+
+import { Button } from '@/components/ui/button'
+import { IconCheck, IconCopy } from '@/components/ui/icons'
+import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
+import cn from 'mxcn'
+
+interface ChatMessageActionsProps extends React.ComponentProps<'div'> {
+ message: Message
+}
+
+export function ChatMessageActions({
+ message,
+ className,
+ ...props
+}: ChatMessageActionsProps) {
+ const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
+
+ const onCopy = () => {
+ if (isCopied) return
+ copyToClipboard(message.content)
+ }
+
+ return (
+
+
+ {isCopied ? : }
+ Copy message
+
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/chat-message.tsx b/examples/agents/ask-my-text-documents-agent/components/chat-message.tsx
new file mode 100755
index 00000000..7d323342
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/chat-message.tsx
@@ -0,0 +1,77 @@
+import { Message } from '@baseai/core/pipes'
+import remarkGfm from 'remark-gfm'
+import remarkMath from 'remark-math'
+
+import { ChatMessageActions } from '@/components/chat-message-actions'
+import { MemoizedReactMarkdown } from '@/components/markdown'
+import { CodeBlock } from '@/components/ui/codeblock'
+import { IconSparkles, IconUser } from '@/components/ui/icons'
+import cn from 'mxcn'
+
+export interface ChatMessageProps {
+ message: Message
+}
+
+export function ChatMessage({ message, ...props }: ChatMessageProps) {
+ return (
+
+
+ {message.role === 'user' ? : }
+
+
+ {children}
+ },
+ code({ node, inline, className, children, ...props }) {
+ if (children.length) {
+ if (children[0] == '▍') {
+ return (
+ ▍
+ )
+ }
+
+ children[0] = (children[0] as string).replace('`▍`', '▍')
+ }
+
+ const match = /language-(\w+)/.exec(className || '')
+
+ if (inline) {
+ return (
+
+ {children}
+
+ )
+ }
+
+ return (
+
+ )
+ }
+ }}
+ >
+ {message.content}
+
+
+
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/chatbot-page.tsx b/examples/agents/ask-my-text-documents-agent/components/chatbot-page.tsx
new file mode 100755
index 00000000..a6301f9c
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/chatbot-page.tsx
@@ -0,0 +1,72 @@
+'use client'
+
+import React, { useState } from 'react';
+import { usePipe } from '@baseai/core/react';
+import cn from 'mxcn';
+import { toast } from 'sonner';
+import { Message } from '@baseai/core/pipes';
+import { ChatList } from '@/components/chat-list';
+import { ChatInput } from './chat-input';
+import { Opening } from './opening';
+import { Welcome } from './welcome';
+import { Suggestions } from './suggestions';
+
+export interface ChatProps extends React.ComponentProps<'div'> {
+ id?: string;
+ initialMessages?: Message[];
+}
+
+export function Chatbot({ id, initialMessages, className }: ChatProps) {
+ const [threadId, setThreadId] = useState(null);
+
+ const {
+ messages,
+ input,
+ handleInputChange,
+ handleSubmit,
+ isLoading,
+ regenerate,
+ stop,
+ sendMessage,
+ } = usePipe({
+ stream: true,
+ apiRoute: '/api/chat',
+ initialMessages,
+ onResponse: (message) => {
+ console.log('Full response:', message);
+ },
+ onError: (error) => {
+ toast.error(error.message);
+ },
+ });
+
+ const sendSuggestedPrompt = (prompt: string) => {
+ handleInputChange({ target: { value: prompt } } as React.ChangeEvent);
+ };
+
+ return (
+
+
+ {messages.length ? (
+
+ ) : (
+ <>
+
+
+
+ >
+ )}
+
+
+
+ );
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/header.tsx b/examples/agents/ask-my-text-documents-agent/components/header.tsx
new file mode 100755
index 00000000..55271258
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/header.tsx
@@ -0,0 +1,55 @@
+import { buttonVariants } from '@/components/ui/button'
+import cn from 'mxcn'
+import Link from 'next/link'
+import { IconDocs, IconFork, IconGitHub } from './ui/icons'
+
+export async function Header() {
+ return (
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/markdown.tsx b/examples/agents/ask-my-text-documents-agent/components/markdown.tsx
new file mode 100755
index 00000000..d4491467
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/markdown.tsx
@@ -0,0 +1,9 @@
+import { FC, memo } from 'react'
+import ReactMarkdown, { Options } from 'react-markdown'
+
+export const MemoizedReactMarkdown: FC = memo(
+ ReactMarkdown,
+ (prevProps, nextProps) =>
+ prevProps.children === nextProps.children &&
+ prevProps.className === nextProps.className
+)
diff --git a/examples/agents/ask-my-text-documents-agent/components/opening.tsx b/examples/agents/ask-my-text-documents-agent/components/opening.tsx
new file mode 100755
index 00000000..aeb78c85
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/opening.tsx
@@ -0,0 +1,65 @@
+import Link from 'next/link'
+
+export function Opening() {
+ return (
+
+
+
+
+
+ Ask My Text Documents Agent
+
+
+
+
+
+
Learn more by checking out:
+
+
+ 1.
+
+ Follow this guide to create an agent to have a conversation with your documents locally and on live Langbase
+
+
+
+ 2.
+ Fork the `ask-my-text-documents-agent` Pipe on ⌘ Langbase
+
+
+
+ 3.
+ Deep dive into the source code
+
+
+ 4.
+ Learn more about BaseAI Memory and Pipes
+
+
+
+
+
+
+ )
+}
+
+// Description Link
+function Dlink({
+ href,
+ children,
+ ...props
+}: {
+ href: string
+ children: React.ReactNode
+ [key: string]: any
+}) {
+ return (
+
+ {children}
+
+ )
+}
\ No newline at end of file
diff --git a/examples/agents/ask-my-text-documents-agent/components/prompt-form.tsx b/examples/agents/ask-my-text-documents-agent/components/prompt-form.tsx
new file mode 100755
index 00000000..44c09329
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/prompt-form.tsx
@@ -0,0 +1,92 @@
+import { Button } from '@/components/ui/button'
+import { IconChat, IconCommand, IconSpinner } from '@/components/ui/icons'
+import { useEnterSubmit } from '@/lib/hooks/use-enter-submit'
+import * as React from 'react'
+import Textarea from 'react-textarea-autosize'
+
+export interface PromptProps {
+ onSubmit: (value: string) => Promise
+ input: string
+ handleInputChange: (e: React.ChangeEvent) => void
+ isLoading: boolean
+}
+
+export function PromptForm({
+ onSubmit,
+ input,
+ handleInputChange,
+ isLoading
+}: PromptProps) {
+ const { formRef, onKeyDown } = useEnterSubmit()
+ const inputRef = React.useRef(null)
+
+ React.useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus()
+ }
+ }, [])
+
+ return (
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/suggestions.tsx b/examples/agents/ask-my-text-documents-agent/components/suggestions.tsx
new file mode 100644
index 00000000..fd2a346d
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/suggestions.tsx
@@ -0,0 +1,60 @@
+import cn from 'mxcn'
+import { IconSparkles } from './ui/icons'
+
+// Prompt suggestions – Change these to match your use-case/company
+const suggestions = [
+ {
+ title: `Concise summary of the attached document`,
+ prompt: `Give a concise summary of the document in the CONTEXT`
+ },
+ {
+ title: `Top 10 most important keywords`,
+ prompt: `Extract top 10 most important keywords in the attached document from the CONTEXT that are essential for insights`
+ },
+ {
+ title: `Insights from the attached document`,
+ prompt: `Extract insights from the attached document in the CONTEXT in bullet points`
+ },
+ {
+ title: `How can I use your services?`,
+ prompt: `How can I use your services?`
+ }
+]
+
+export const Suggestions = ({
+ sendSuggestedPrompt
+}: {
+ sendSuggestedPrompt: (prompt: string) => void
+}) => {
+ const handleClick = (prompt: string) => {
+ sendSuggestedPrompt(prompt)
+ }
+
+ return (
+
+
Suggestions
+
+ {suggestions.map((suggestion, index) => {
+ return (
+
handleClick(suggestion.prompt)}
+ >
+
+
+ {suggestion.title}
+
+
+ )
+ })}
+
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/ui/button.tsx b/examples/agents/ask-my-text-documents-agent/components/ui/button.tsx
new file mode 100755
index 00000000..73643111
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/ui/button.tsx
@@ -0,0 +1,70 @@
+import { Slot } from '@radix-ui/react-slot'
+import { cva, type VariantProps } from 'class-variance-authority'
+import * as React from 'react'
+
+import cn from 'mxcn'
+
+const buttonVariants = cva(
+ 'focus-visible:ring-ring-muted-foreground/25 inline-flex cursor-pointer select-none items-center justify-center rounded-lg text-sm font-medium transition-colors focus:ring-1 focus:ring-muted-foreground/25 focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 gap-2 group',
+ {
+ variants: {
+ variant: {
+ default:
+ 'border border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/90',
+ warn: 'bg-warning text-warning-foreground hover:bg-warning/90 shadow-sm',
+ destructive:
+ 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
+ 'destructive-hover':
+ 'border border-input bg-muted font-bold text-destructive shadow-sm hover:bg-destructive hover:text-destructive-foreground',
+ 'outline-background':
+ 'border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-foreground hover:text-background',
+ 'outline-inverse':
+ 'border border-input bg-muted-foreground text-muted shadow-sm hover:bg-foreground hover:text-background',
+ outline:
+ 'border border-input bg-transparent shadow-sm hover:bg-foreground hover:text-background',
+ 'outline-muted':
+ 'border border-input bg-muted text-foreground shadow-sm hover:bg-foreground hover:text-background',
+ secondary:
+ 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
+ link: 'text-primary underline-offset-4 hover:underline',
+ green:
+ 'rounded-lg bg-green-500 text-primary shadow-sm hover:bg-green-400 dark:bg-green-700 dark:hover:bg-green-800'
+ },
+ size: {
+ default: 'h-9 px-4 py-2',
+ xs: 'h-6 rounded-lg px-2 text-xs',
+ sm: 'h-8 rounded-lg px-3 text-xs',
+ lg: 'h-10 rounded-lg px-8',
+ xl: 'h-14 rounded-lg px-10',
+ icon: 'h-9 w-9'
+ }
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default'
+ }
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : 'button'
+ return (
+
+ )
+ }
+)
+Button.displayName = 'Button'
+
+export { Button, buttonVariants }
diff --git a/examples/agents/ask-my-text-documents-agent/components/ui/codeblock.tsx b/examples/agents/ask-my-text-documents-agent/components/ui/codeblock.tsx
new file mode 100755
index 00000000..a2e266a6
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/ui/codeblock.tsx
@@ -0,0 +1,145 @@
+// Inspired by Chatbot-UI and modified to fit the needs of this project
+// @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Markdown/CodeBlock.tsx
+
+'use client'
+
+import { FC, memo } from 'react'
+import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
+import { coldarkDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
+
+import { Button } from '@/components/ui/button'
+import { IconCheck, IconCopy, IconDownload } from '@/components/ui/icons'
+import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
+
+interface Props {
+ language: string
+ value: string
+}
+
+interface languageMap {
+ [key: string]: string | undefined
+}
+
+export const programmingLanguages: languageMap = {
+ javascript: '.js',
+ python: '.py',
+ java: '.java',
+ c: '.c',
+ cpp: '.cpp',
+ 'c++': '.cpp',
+ 'c#': '.cs',
+ ruby: '.rb',
+ php: '.php',
+ swift: '.swift',
+ 'objective-c': '.m',
+ kotlin: '.kt',
+ typescript: '.ts',
+ go: '.go',
+ perl: '.pl',
+ rust: '.rs',
+ scala: '.scala',
+ haskell: '.hs',
+ lua: '.lua',
+ shell: '.sh',
+ sql: '.sql',
+ html: '.html',
+ css: '.css'
+ // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component
+}
+
+export const generateRandomString = (length: number, lowercase = false) => {
+ const chars = 'ABCDEFGHJKLMNPQRSTUVWXY3456789' // excluding similar looking characters like Z, 2, I, 1, O, 0
+ let result = ''
+ for (let i = 0; i < length; i++) {
+ result += chars.charAt(Math.floor(Math.random() * chars.length))
+ }
+ return lowercase ? result.toLowerCase() : result
+}
+
+const CodeBlock: FC = memo(({ language, value }) => {
+ const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
+
+ const downloadAsFile = () => {
+ if (typeof window === 'undefined') {
+ return
+ }
+ const fileExtension = programmingLanguages[language] || '.file'
+ const suggestedFileName = `file-${generateRandomString(
+ 3,
+ true
+ )}${fileExtension}`
+ const fileName = window.prompt('Enter file name' || '', suggestedFileName)
+
+ if (!fileName) {
+ // User pressed cancel on prompt.
+ return
+ }
+
+ const blob = new Blob([value], { type: 'text/plain' })
+ const url = URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.download = fileName
+ link.href = url
+ link.style.display = 'none'
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ URL.revokeObjectURL(url)
+ }
+
+ const onCopy = () => {
+ if (isCopied) return
+ copyToClipboard(value)
+ }
+
+ return (
+
+
+
{language}
+
+
+
+ Download
+
+
+ {isCopied ? : }
+ Copy code
+
+
+
+
+ {value}
+
+
+ )
+})
+CodeBlock.displayName = 'CodeBlock'
+
+export { CodeBlock }
diff --git a/examples/agents/ask-my-text-documents-agent/components/ui/icons.tsx b/examples/agents/ask-my-text-documents-agent/components/ui/icons.tsx
new file mode 100755
index 00000000..8ac8488f
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/ui/icons.tsx
@@ -0,0 +1,399 @@
+'use client'
+
+import cn from 'mxcn'
+import * as React from 'react'
+
+function IconOpenAI({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+ OpenAI icon
+
+
+ )
+}
+
+function IconGitHub({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+ GitHub
+
+
+ )
+}
+
+function IconArrowRight({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconUser(props: JSX.IntrinsicElements['svg']) {
+ return (
+
+
+
+ )
+}
+
+function IconSpinner({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconMessage({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconRefresh({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+export function IconRegenerate(props: JSX.IntrinsicElements['svg']) {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function IconStop({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconCopy({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconCheck({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconDownload({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconClose({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconSparkles({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+export function IconChat(props: JSX.IntrinsicElements['svg']) {
+ return (
+
+
+
+ )
+}
+
+function IconInfo({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+function IconFork({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+export function IconCommand(props: JSX.IntrinsicElements['svg']) {
+ return (
+
+
+
+ )
+}
+
+export function IconDocs(props: JSX.IntrinsicElements['svg']) {
+ return (
+
+
+
+
+
+ )
+
+ // return (
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // )
+}
+
+export {
+ IconArrowRight,
+ IconCheck,
+ IconClose,
+ IconCopy,
+ IconDownload,
+ IconFork,
+ IconGitHub,
+ IconInfo,
+ IconMessage,
+ IconOpenAI,
+ IconRefresh,
+ IconSparkles,
+ IconSpinner,
+ IconStop,
+ IconUser
+}
diff --git a/examples/agents/ask-my-text-documents-agent/components/ui/separator.tsx b/examples/agents/ask-my-text-documents-agent/components/ui/separator.tsx
new file mode 100755
index 00000000..7ba06b3a
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/ui/separator.tsx
@@ -0,0 +1,32 @@
+'use client'
+
+import * as React from 'react'
+import * as SeparatorPrimitive from '@radix-ui/react-separator'
+import cn from 'mxcn'
+
+const Separator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(
+ (
+ { className, orientation = 'horizontal', decorative = true, ...props },
+ ref
+ ) => (
+
+ )
+)
+Separator.displayName = SeparatorPrimitive.Root.displayName
+
+export { Separator }
diff --git a/examples/agents/ask-my-text-documents-agent/components/ui/textarea.tsx b/examples/agents/ask-my-text-documents-agent/components/ui/textarea.tsx
new file mode 100755
index 00000000..93b82cb8
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/ui/textarea.tsx
@@ -0,0 +1,23 @@
+import cn from 'mxcn'
+import * as React from 'react'
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Textarea.displayName = 'Textarea'
+
+export { Textarea }
diff --git a/examples/agents/ask-my-text-documents-agent/components/welcome.tsx b/examples/agents/ask-my-text-documents-agent/components/welcome.tsx
new file mode 100644
index 00000000..440e75f9
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/components/welcome.tsx
@@ -0,0 +1,16 @@
+import { IconDocs } from './ui/icons'
+
+export const Welcome = () => {
+ return (
+
+
+
+ Ask My Text Documents Agent
+
+
+ Ask My Text Documents Agent example is built with BaseAI framework that enables question and answering from your docs
+ using Langbase agentic Pipes and Memory. The example supports conversation with .pdf, .txt, .md format.
+
+
+ )
+}
diff --git a/examples/agents/ask-my-text-documents-agent/env.d.ts b/examples/agents/ask-my-text-documents-agent/env.d.ts
new file mode 100644
index 00000000..68a2a989
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/env.d.ts
@@ -0,0 +1,5 @@
+// Generated by Wrangler
+// by running `wrangler types --env-interface CloudflareEnv env.d.ts`
+
+interface CloudflareEnv {
+}
diff --git a/examples/agents/ask-my-text-documents-agent/lib/hooks/use-copy-to-clipboard.tsx b/examples/agents/ask-my-text-documents-agent/lib/hooks/use-copy-to-clipboard.tsx
new file mode 100755
index 00000000..62f7156d
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/lib/hooks/use-copy-to-clipboard.tsx
@@ -0,0 +1,33 @@
+'use client'
+
+import * as React from 'react'
+
+export interface useCopyToClipboardProps {
+ timeout?: number
+}
+
+export function useCopyToClipboard({
+ timeout = 2000
+}: useCopyToClipboardProps) {
+ const [isCopied, setIsCopied] = React.useState(false)
+
+ const copyToClipboard = (value: string) => {
+ if (typeof window === 'undefined' || !navigator.clipboard?.writeText) {
+ return
+ }
+
+ if (!value) {
+ return
+ }
+
+ navigator.clipboard.writeText(value).then(() => {
+ setIsCopied(true)
+
+ setTimeout(() => {
+ setIsCopied(false)
+ }, timeout)
+ })
+ }
+
+ return { isCopied, copyToClipboard }
+}
diff --git a/examples/agents/ask-my-text-documents-agent/lib/hooks/use-enter-submit.tsx b/examples/agents/ask-my-text-documents-agent/lib/hooks/use-enter-submit.tsx
new file mode 100755
index 00000000..d66b2d32
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/lib/hooks/use-enter-submit.tsx
@@ -0,0 +1,23 @@
+import { useRef, type RefObject } from 'react'
+
+export function useEnterSubmit(): {
+ formRef: RefObject
+ onKeyDown: (event: React.KeyboardEvent) => void
+} {
+ const formRef = useRef(null)
+
+ const handleKeyDown = (
+ event: React.KeyboardEvent
+ ): void => {
+ if (
+ event.key === 'Enter' &&
+ !event.shiftKey &&
+ !event.nativeEvent.isComposing
+ ) {
+ formRef.current?.requestSubmit()
+ event.preventDefault()
+ }
+ }
+
+ return { formRef, onKeyDown: handleKeyDown }
+}
diff --git a/examples/agents/ask-my-text-documents-agent/lib/types.ts b/examples/agents/ask-my-text-documents-agent/lib/types.ts
new file mode 100755
index 00000000..cd0a5f03
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/lib/types.ts
@@ -0,0 +1,11 @@
+import { type Message } from 'ai'
+
+export interface Chat extends Record {
+ id: string
+ title: string
+ createdAt: Date
+ userId: string
+ path: string
+ messages: Message[]
+ sharePath?: string
+}
diff --git a/examples/agents/ask-my-text-documents-agent/lib/utils.ts b/examples/agents/ask-my-text-documents-agent/lib/utils.ts
new file mode 100755
index 00000000..a5cc2c68
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/lib/utils.ts
@@ -0,0 +1,8 @@
+export function formatDate(input: string | number | Date): string {
+ const date = new Date(input)
+ return date.toLocaleDateString('en-US', {
+ month: 'long',
+ day: 'numeric',
+ year: 'numeric'
+ })
+}
diff --git a/examples/agents/ask-my-text-documents-agent/next.config.mjs b/examples/agents/ask-my-text-documents-agent/next.config.mjs
new file mode 100644
index 00000000..2a73efec
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/next.config.mjs
@@ -0,0 +1,13 @@
+import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev';
+
+// Here we use the @cloudflare/next-on-pages next-dev module to allow us to use bindings during local development
+// (when running the application with `next dev`), for more information see:
+// https://github.com/cloudflare/next-on-pages/blob/main/internal-packages/next-dev/README.md
+if (process.env.NODE_ENV === 'development') {
+ await setupDevPlatform();
+}
+
+/** @type {import('next').NextConfig} */
+const nextConfig = {};
+
+export default nextConfig;
diff --git a/examples/agents/ask-my-text-documents-agent/package.json b/examples/agents/ask-my-text-documents-agent/package.json
new file mode 100644
index 00000000..42ecf6b7
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "ask-my-text-documents-agent",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint",
+ "pages:build": "npx @cloudflare/next-on-pages",
+ "preview": "npm run pages:build && wrangler pages dev",
+ "deploy": "npm run pages:build && wrangler pages deploy",
+ "cf-typegen": "wrangler types --env-interface CloudflareEnv env.d.ts",
+ "baseai": "baseai"
+ },
+ "dependencies": {
+ "@baseai/core": "^0.9.8",
+ "@tailwindcss/typography": "github:tailwindcss/typography",
+ "ai": "^3.4.9",
+ "class-variance-authority": "^0.7.0",
+ "mxcn": "^2.0.0",
+ "next": "14.2.5",
+ "openai": "^4.67.3",
+ "radix-ui": "^1.0.1",
+ "react": "^18",
+ "react-dom": "^18",
+ "react-markdown": "^9.0.1",
+ "react-syntax-highlighter": "^15.5.0",
+ "react-textarea-autosize": "^8.5.4",
+ "remark-gfm": "^4.0.0",
+ "remark-math": "^6.0.0",
+ "sonner": "^1.5.0"
+ },
+ "devDependencies": {
+ "@cloudflare/next-on-pages": "^1.13.5",
+ "@cloudflare/workers-types": "^4.20241011.0",
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "baseai": "^0.9.8",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.5",
+ "eslint-plugin-next-on-pages": "^1.13.5",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.13",
+ "typescript": "^5",
+ "vercel": "^37.8.0",
+ "wrangler": "^3.80.4"
+ }
+}
diff --git a/examples/agents/ask-my-text-documents-agent/postcss.config.mjs b/examples/agents/ask-my-text-documents-agent/postcss.config.mjs
new file mode 100644
index 00000000..1a69fd2a
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ tailwindcss: {},
+ },
+};
+
+export default config;
diff --git a/examples/agents/ask-my-text-documents-agent/public/ask-my-text-documents-agent.jpg b/examples/agents/ask-my-text-documents-agent/public/ask-my-text-documents-agent.jpg
new file mode 100644
index 00000000..6265c85c
Binary files /dev/null and b/examples/agents/ask-my-text-documents-agent/public/ask-my-text-documents-agent.jpg differ
diff --git a/examples/agents/ask-my-text-documents-agent/tailwind.config.ts b/examples/agents/ask-my-text-documents-agent/tailwind.config.ts
new file mode 100644
index 00000000..8a0ab881
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/tailwind.config.ts
@@ -0,0 +1,177 @@
+const { fontFamily } = require('tailwindcss/defaultTheme')
+import type { Config } from 'tailwindcss'
+
+const config: Config = {
+ darkMode: 'selector',
+ content: [
+ './pages/**/*.{js,ts,jsx,tsx,mdx}',
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
+ './app/**/*.{js,ts,jsx,tsx,mdx}'
+ ],
+ theme: {
+ transparent: 'transparent',
+ current: 'currentColor',
+ extend: {
+ backgroundImage: {
+ 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
+ 'gradient-conic':
+ 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))'
+ },
+ colors: {
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))'
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))'
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))'
+ },
+ warning: {
+ DEFAULT: 'hsl(var(--warning))',
+ foreground: 'hsl(var(--warning-foreground))'
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))'
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))'
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))'
+ },
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))'
+ },
+ // light mode
+ tremor: {
+ brand: {
+ faint: '#eff6ff', // blue-50
+ muted: '#bfdbfe', // blue-200
+ subtle: '#60a5fa', // blue-400
+ DEFAULT: '#3b82f6', // blue-500
+ emphasis: '#1d4ed8', // blue-700
+ inverted: '#ffffff' // white
+ },
+ background: {
+ muted: '#f9fafb', // gray-50
+ subtle: '#f3f4f6', // gray-100
+ DEFAULT: '#ffffff', // white
+ emphasis: '#374151' // gray-700
+ },
+ border: {
+ DEFAULT: '#e5e7eb' // gray-200
+ },
+ ring: {
+ DEFAULT: '#e5e7eb' // gray-200
+ },
+ content: {
+ subtle: '#9ca3af', // gray-400
+ DEFAULT: '#6b7280', // gray-500
+ emphasis: '#374151', // gray-700
+ strong: '#111827', // gray-900
+ inverted: '#ffffff' // white
+ }
+ },
+ // dark mode
+ 'dark-tremor': {
+ brand: {
+ faint: 'hsl(var(--background))', // custom
+ muted: 'hsl(var(--muted))', // blue-950
+ subtle: '#1e40af', // blue-800
+ DEFAULT: '#3b82f6', // blue-500
+ emphasis: '#60a5fa', // blue-400
+ inverted: 'hsl(var(--muted))' // gray-950
+ },
+ background: {
+ muted: 'hsl(var(--muted))', // custom
+ subtle: 'hsl(var(--muted))', // gray-800
+ DEFAULT: 'hsl(var(--background))', // gray-900
+ emphasis: '#d1d5db' // gray-300
+ },
+ border: {
+ DEFAULT: 'hsl(var(--border))' // gray-800
+ },
+ ring: {
+ DEFAULT: 'hsl(var(--muted))' // gray-800
+ },
+ content: {
+ subtle: '#4b5563', // gray-600
+ DEFAULT: '#6b7280', // gray-600
+ emphasis: '#e5e7eb', // gray-200
+ strong: '#f9fafb', // gray-50
+ inverted: '#000000' // black
+ }
+ }
+ },
+ boxShadow: {
+ // light
+ 'tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
+ 'tremor-card':
+ '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
+ 'tremor-dropdown':
+ '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
+ // dark
+ 'dark-tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
+ 'dark-tremor-card':
+ '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
+ 'dark-tremor-dropdown':
+ '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
+ },
+ borderRadius: {
+ lg: `var(--radius)`,
+ md: `calc(var(--radius) - 2px)`,
+ sm: 'calc(var(--radius) - 4px)',
+ 'tremor-small': '0.375rem',
+ 'tremor-default': '0.5rem',
+ 'tremor-full': '9999px'
+ },
+ fontSize: {
+ // 'tremor-label': ['0.75rem'],
+ 'tremor-label': '0.75rem',
+ 'tremor-default': ['0.875rem', { lineHeight: '1.25rem' }],
+ 'tremor-title': ['1.125rem', { lineHeight: '1.75rem' }],
+ 'tremor-metric': ['1.875rem', { lineHeight: '2.25rem' }]
+ },
+ fontFamily: {
+ sans: ['var(--font-sans)', ...fontFamily.sans]
+ },
+ keyframes: {
+ 'accordion-down': {
+ from: { height: '0' },
+ to: { height: 'var(--radix-accordion-content-height)' }
+ },
+ 'accordion-up': {
+ from: { height: 'var(--radix-accordion-content-height)' },
+ to: { height: '0' }
+ },
+
+ slide: {
+ to: {
+ transform: 'translate(calc(100cqw - 100%), 0)'
+ }
+ }
+ },
+ animation: {
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out',
+ // spin: 'spin calc(var(--speed) * 2) infinite linear',
+ slide: 'slide var(--speed) ease-in-out infinite alternate'
+ }
+ }
+ },
+ plugins: [require('@tailwindcss/typography')]
+}
+export default config
diff --git a/examples/agents/ask-my-text-documents-agent/tsconfig.json b/examples/agents/ask-my-text-documents-agent/tsconfig.json
new file mode 100644
index 00000000..574a1c8c
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ },
+ "types": [
+ "@cloudflare/workers-types/2023-07-01"
+ ]
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/examples/agents/ask-my-text-documents-agent/wrangler.toml b/examples/agents/ask-my-text-documents-agent/wrangler.toml
new file mode 100644
index 00000000..bad71d34
--- /dev/null
+++ b/examples/agents/ask-my-text-documents-agent/wrangler.toml
@@ -0,0 +1,87 @@
+#:schema node_modules/wrangler/config-schema.json
+name = "ask-my-text-documents-agent"
+compatibility_date = "2024-10-11"
+compatibility_flags = ["nodejs_compat"]
+pages_build_output_dir = ".vercel/output/static"
+
+# Automatically place your workloads in an optimal location to minimize latency.
+# If you are running back-end logic in a Pages Function, running it closer to your back-end infrastructure
+# rather than the end user may result in better performance.
+# Docs: https://developers.cloudflare.com/pages/functions/smart-placement/#smart-placement
+# [placement]
+# mode = "smart"
+
+# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables)
+# Docs:
+# - https://developers.cloudflare.com/pages/functions/bindings/#environment-variables
+# Note: Use secrets to store sensitive data.
+# - https://developers.cloudflare.com/pages/functions/bindings/#secrets
+# [vars]
+# MY_VARIABLE = "production_value"
+
+# Bind the Workers AI model catalog. Run machine learning models, powered by serverless GPUs, on Cloudflare’s global network
+# Docs: https://developers.cloudflare.com/pages/functions/bindings/#workers-ai
+# [ai]
+# binding = "AI"
+
+# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.
+# Docs: https://developers.cloudflare.com/pages/functions/bindings/#d1-databases
+# [[d1_databases]]
+# binding = "MY_DB"
+# database_name = "my-database"
+# database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+
+# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
+# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
+# Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects
+# [[durable_objects.bindings]]
+# name = "MY_DURABLE_OBJECT"
+# class_name = "MyDurableObject"
+# script_name = 'my-durable-object'
+
+# Bind a KV Namespace. Use KV as persistent storage for small key-value pairs.
+# Docs: https://developers.cloudflare.com/pages/functions/bindings/#kv-namespaces
+# KV Example:
+# [[kv_namespaces]]
+# binding = "MY_KV_NAMESPACE"
+# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+# Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer.
+# Docs: https://developers.cloudflare.com/pages/functions/bindings/#queue-producers
+# [[queues.producers]]
+# binding = "MY_QUEUE"
+# queue = "my-queue"
+
+# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files.
+# Docs: https://developers.cloudflare.com/pages/functions/bindings/#r2-buckets
+# [[r2_buckets]]
+# binding = "MY_BUCKET"
+# bucket_name = "my-bucket"
+
+# Bind another Worker service. Use this binding to call another Worker without network overhead.
+# Docs: https://developers.cloudflare.com/pages/functions/bindings/#service-bindings
+# [[services]]
+# binding = "MY_SERVICE"
+# service = "my-service"
+
+# To use different bindings for preview and production environments, follow the examples below.
+# When using environment-specific overrides for bindings, ALL bindings must be specified on a per-environment basis.
+# Docs: https://developers.cloudflare.com/pages/functions/wrangler-configuration#environment-specific-overrides
+
+######## PREVIEW environment config ########
+
+# [env.preview.vars]
+# API_KEY = "xyz789"
+
+# [[env.preview.kv_namespaces]]
+# binding = "MY_KV_NAMESPACE"
+# id = ""
+
+######## PRODUCTION environment config ########
+
+# [env.production.vars]
+# API_KEY = "abc123"
+
+# [[env.production.kv_namespaces]]
+# binding = "MY_KV_NAMESPACE"
+# id = ""