diff --git a/README.md b/README.md index 9ab8d36ba..8fbf5141b 100644 --- a/README.md +++ b/README.md @@ -35,23 +35,19 @@ +

+⚑ Build AI agents instantly with natural language | πŸ”Œ Connect tools with one-click integrations | πŸ“‚ Power with knowledge by adding documents for RAG | πŸ”„ Automate workflows by setting up triggers and actions | πŸš€ Deploy anywhere via API or SDK

+☁️ Prefer a hosted version? Use our cloud to starting building agents right away! +

-- ✨ **Start from an idea -> copilot builds your multi-agent workflows** - - E.g. "Build me an assistant for a food delivery company to handle delivery status and missing items. Include the necessary tools." -- 🌐 **Connect MCP servers** - - Add the MCP servers in settings -> import the tools into Rowboat. -- πŸ“ž **Integrate into your app using the HTTP API or Python SDK** - - Grab the project ID and generated API key from settings and use the API. - -Powered by OpenAI's Agents SDK, Rowboat is the fastest way to build multi-agents! ## Quick start 1. Set your OpenAI key - ```bash - export OPENAI_API_KEY=your-openai-api-key + ```bash + export OPENAI_API_KEY=your-openai-api-key ``` -2. Clone the repository and start Rowboat +2. Clone the repository and start Rowboat (requires Docker) ```bash git clone git@github.com:rowboatlabs/rowboat.git cd rowboat @@ -60,71 +56,30 @@ Powered by OpenAI's Agents SDK, Rowboat is the fastest way to build multi-agents 3. Access the app at [http://localhost:3000](http://localhost:3000). -Note: We have added native RAG support including file-uploads and URL scraping. See the [RAG](https://docs.rowboatlabs.com/using_rag) section of our docs for this. - -Note: See the [Using custom LLM providers](https://docs.rowboatlabs.com/setup/#using-custom-llm-providers) section of our docs for using custom providers like OpenRouter and LiteLLM. +To add tools, RAG, more LLMs, and triggers checkout the [Advanced](#advanced) section below. -## Demo +## Demos +#### Meeting-prep assistant +Chat with the copilot to build a meeting-prep workflow, then add a calendar invite as a trigger. Watch the full demo [here](https://youtu.be/KZTP4xZM2DY). +[![meeting-prep](https://github.com/user-attachments/assets/27755ef5-6549-476f-b9c0-50bef8770384)](https://youtu.be/KZTP4xZM2DY) -#### Create a multi-agent assistant with MCP tools by chatting with Rowboat -[![Screenshot 2025-04-23 at 00 25 31](https://github.com/user-attachments/assets/c8a41622-8e0e-459f-becb-767503489866)](https://youtu.be/YRTCw9UHRbU) +#### Customer support assistant +Chat with the copilot to build a customer support assistant, then connect your MCP server, and data for RAG. Watch the full demo [here](https://youtu.be/Xfo-OfgOl8w). +[![output](https://github.com/user-attachments/assets/97485fd7-64c3-4d60-a627-f756a89dee64)](https://youtu.be/Xfo-OfgOl8w) -## Integrate with Rowboat agents +#### Personal assistant +Chat with the copilot to build a personal assistant. Watch the full demo [here](https://youtu.be/6r7P4Vlcn2g). +[![personal-assistant](https://github.com/user-attachments/assets/0f1c0ffd-23ba-4b49-8bfb-ec7a846f1332)](https://youtu.be/6r7P4Vlcn2g) -There are 2 ways to integrate with the agents you create in Rowboat +## Advanced +1. Native RAG Support: Enable file uploads and URL scraping with Rowboat's built-in RAG capabilities – see [RAG Guide](https://docs.rowboatlabs.com/docs/using-rowboat/rag). -1. HTTP API - - You can use the API directly at [http://localhost:3000/api/v1/](http://localhost:3000/api/v1/) - - See [API Docs](https://docs.rowboatlabs.com/using_the_api/) for details - ```bash - curl --location 'http://localhost:3000/api/v1//chat' \ - --header 'Content-Type: application/json' \ - --header 'Authorization: Bearer ' \ - --data '{ - "messages": [ - { - "role": "user", - "content": "tell me the weather in london in metric units" - } - ], - "state": null - }' - ``` - +2. Custom LLM Providers: Use any LLM provider, including aggregators like OpenRouter and LiteLLM - see [Using more LLM providers](https://docs.rowboatlabs.com/docs/using-rowboat/customise/custom-llms). -2. Python SDK - You can use the included Python SDK to interact with the Agents - ``` - pip install rowboat - ``` +3. Tools & Triggers: Add tools and event triggers (e.g., Gmail, Slack) for automation – see [Tools](https://docs.rowboatlabs.com/docs/using-rowboat/tools) & [Triggers](https://docs.rowboatlabs.com/docs/using-rowboat/triggers). - See [SDK Docs](https://docs.rowboatlabs.com/using_the_sdk/) for details. Here is a quick example: - ```python - from rowboat import Client, StatefulChat - from rowboat.schema import UserMessage, SystemMessage - - # Initialize the client - client = Client( - host="http://localhost:3000", - project_id="", - api_key="" - ) - - # Create a stateful chat session (recommended) - chat = StatefulChat(client) - response = chat.run("What's the weather in London?") - print(response) - - # Or use the low-level client API - messages = [ - SystemMessage(role='system', content="You are a helpful assistant"), - UserMessage(role='user', content="Hello, how are you?") - ] - - # Get response - response = client.chat(messages=messages) - print(response.messages[-1].content) - ``` +4. API & SDK: Integrate Rowboat agents directly into your app – see [API](https://docs.rowboatlabs.com/docs/api-sdk/using_the_api) & [SDK](https://docs.rowboatlabs.com/docs/api-sdk/using_the_sdk) docs. +## Refer to [Docs](https://docs.rowboatlabs.com/) to learn how to start building agents with Rowboat. diff --git a/apps/docs/docs.json b/apps/docs/docs.json new file mode 100644 index 000000000..206579f0d --- /dev/null +++ b/apps/docs/docs.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "theme": "maple", + "name": "Rowboat", + "description": "Rowboat is an open-source platform for building multi-agent systems. It helps you orchestrate tools, RAG, memory, and deployable agents with ease.", + "colors": { + "primary": "#6366F1", + "light": "#6366F1", + "dark": "#6366F1" + }, + "icons": { + "library": "fontawesome" + }, + "navigation": { + "groups": [ + { + "group": "Getting Started", + "pages": [ + "docs/getting-started/introduction", + "docs/getting-started/quickstart", + "docs/getting-started/license" + ] + }, + { + "group": "Using Rowboat", + "pages": [ + "docs/using-rowboat/rowboat-studio", + "docs/using-rowboat/agents", + "docs/using-rowboat/tools", + "docs/using-rowboat/rag", + "docs/using-rowboat/triggers", + "docs/using-rowboat/jobs", + "docs/using-rowboat/conversations", + { + "group": "Customise", + "icon": "sliders", + "pages": [ + "docs/using-rowboat/customise/custom-llms" + ] + } + ] + }, + { + "group": "API & SDK", + "pages": ["docs/api-sdk/using_the_api", "docs/api-sdk/using_the_sdk"] + }, + { + "group": "Development", + "pages": ["docs/development/contribution-guide", "docs/development/roadmap"] + } + ] + }, + "background": { + "decoration": "gradient", + "color": { + "light": "#FFFFFF", + "dark": "#0D0A09" + } + }, + "navbar": { + "primary": { + "type": "button", + "label": "Try Rowboat", + "href": "https://app.rowboatlabs.com" + } + }, + "footer": { + "socials": { + "github": "https://github.com/rowboatlabs/rowboat", + "linkedin": "https://www.linkedin.com/company/rowboat-labs", + "discord": "https://discord.gg/PCkH9TWC" + } + }, + "contextual": { + "options": [ + "copy", + "view", + "chatgpt", + "claude" + ] + } + } + \ No newline at end of file diff --git a/apps/docs/docs/add_tools.mdx b/apps/docs/docs/add_tools.mdx new file mode 100644 index 000000000..f88ce7100 --- /dev/null +++ b/apps/docs/docs/add_tools.mdx @@ -0,0 +1,27 @@ +## Add tools to agents +Copilot can help you add tools to agents. You can (a) add a mock tool, (b) add a tool from an MCP server, (c) integrate with you own tools using a webhook. + + +### Adding mock tools +You can mock any tool you have created by checking the 'Mock tool responses' option. + + +![Example Tool](img/mock-tool.png) + +### Adding MCP tools + +You can add a running MCP server in Settings -> Tools. + +![Example Tool](img/add-mcp-server.png) + +You can use [supergateway](https://github.com/supercorp-ai/supergateway) to expose any MCP stdio server as an SSE server. + +Now, you can import the tools from the MCP server in the Build view. + +![Example Tool](img/import-mcp-tools.png) + + +### Debug tool calls in the playground +When agents call tools during a chat in the playground, the tool call parameters and response are available for debugging real-time. For testing purposes, the platform can produce mock tool responses in the playground, without integrating actual tools. + +![Mock Tool Responses](img/mock-response.png) \ No newline at end of file diff --git a/apps/docs/docs/agents.mdx b/apps/docs/docs/agents.mdx new file mode 100644 index 000000000..6ffb91a39 --- /dev/null +++ b/apps/docs/docs/agents.mdx @@ -0,0 +1,29 @@ +# Agents + +## Overview +- Agents carry out a specific part of the conversation and / or perform tasks like orchestrating between other agents, triggering internal processes and fetching information. +- Agents carry out tasks through tools provided to them. +- Agents can be connected to other agents through a mention in the agent's instruction. + +## Agent Configurations + +### Description +The description conveys the agent's role in the multi-agent system. Writing a good description is important for other agents to know when to pass control of the conversation to an agent. + +### Instructions +Agent instructions are the backbone of an agent, defining its behavior. RowBoat Studio's copilot produces a good framework for agent instructions, involving Role, Steps to Follow, Scope and Guidelines. Since agents are powered by LLMs, general best practices while writing prompts apply. + +### Examples +The agent uses examples as a reference for behavior in different scenarios. While there are no prescribed formats to provide examples in, examples should include what the user might say, what the agent should respond with as well as indications of any tool calls to be made. + +### Prompts +Prompts attached to an agent will be used by the agent in addition to instructions. + +### Tools +Tools attached to an agent will be put out as tool calls. The behavior of when to invoke tools can be fine-tuned by specifying corresponding instructions or prompts. Adding examples to agents can also be useful in controlling tool call behavior. + +### Connected Agents +In the agent instructions, the connected agents are shown with an '@mention'. If the agent mentioned in an instruction (connected agent) does not actually exist, the connected agent's name would show up with an '!' to call to attention. + +### Model +RowBoat currently supports OpenAI LLMs. Agents can be configured to use GPT-4o or GPT-4o-mini. \ No newline at end of file diff --git a/apps/docs/docs/api-sdk/using_the_api.mdx b/apps/docs/docs/api-sdk/using_the_api.mdx new file mode 100644 index 000000000..60125b909 --- /dev/null +++ b/apps/docs/docs/api-sdk/using_the_api.mdx @@ -0,0 +1,173 @@ +--- +title: "Using the API" +description: "This is a guide on using the HTTP API to power conversations with the assistant created in Studio." +icon: "code" +--- + + +## Deploy your assistant to production on Studio + + Prod Deploy + + +## Obtain API key and Project ID + +Generate API keys via the developer configs in your project. Copy the Project ID from the same page. + + Developer Configs + + +## API Endpoint + +``` +POST /api/v1//chat +``` + +Where: + +- For self-hosted: `` is `http://localhost:3000` + +## Authentication + +Include your API key in the Authorization header: + +``` +Authorization: Bearer +``` + +## Examples + +### First Turn + +```bash +curl --location '/api/v1//chat' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer ' \ +--data '{ + "messages": [ + { + "role": "user", + "content": "Hello, can you help me?" + } + ], + "state": null +}' +``` + +Response: +```json +{ + "messages": [ + { + "role": "assistant", + "content": "Hello! Yes, I'd be happy to help you. What can I assist you with today?", + "agenticResponseType": "external" + } + ], + "state": { + "last_agent_name": "MainAgent" + } +} +``` + +### Subsequent Turn + +Notice how we include both the previous messages and the state from the last response: + +```bash +curl --location '/api/v1//chat' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer ' \ +--data '{ + "messages": [ + { + "role": "user", + "content": "Hello, can you help me?" + }, + { + "role": "assistant", + "content": "Hello! Yes, I'd be happy to help you. What can I assist you with today?", + "agenticResponseType": "external" + }, + { + "role": "user", + "content": "What services do you offer?" + } + ], + "state": { + "last_agent_name": "MainAgent" + } +}' +``` + +## API Specification + +### Request Schema + +```typescript +{ + // Required fields + messages: Message[]; // Array of message objects representing the conversation history + state: any; // State object from previous response, or null for first message + + // Optional fields + workflowId?: string; // Specific workflow ID to use (defaults to production workflow) + testProfileId?: string; // Test profile ID for simulation +} +``` + +### Message Types + +Messages can be one of the following types: + +1. System Message +```typescript +{ + role: "system"; + content: string; +} +``` + +2. User Message +```typescript +{ + role: "user"; + content: string; +} +``` + +3. Assistant Message +```typescript +{ + role: "assistant"; + content: string; + agenticResponseType: "internal" | "external"; + agenticSender?: string | null; +} +``` + +### Response Schema + +```typescript +{ + messages: Message[]; // Array of new messages from this turn + state: any; // State object to pass in the next request +} +``` + +## Important Notes + +1. Always pass the complete conversation history in the `messages` array +2. Always include the `state` from the previous response in your next request +3. The last message in the response's `messages` array will be a user-facing assistant message (`agenticResponseType: "external"`) + +## Rate Limiting + +The API has rate limits per project. If exceeded, you'll receive a 429 status code. + +## Error Responses + +- 400: Invalid request body or missing/invalid Authorization header +- 403: Invalid API key +- 404: Project or workflow not found +- 429: Rate limit exceeded \ No newline at end of file diff --git a/apps/docs/docs/api-sdk/using_the_sdk.mdx b/apps/docs/docs/api-sdk/using_the_sdk.mdx new file mode 100644 index 000000000..56a61f995 --- /dev/null +++ b/apps/docs/docs/api-sdk/using_the_sdk.mdx @@ -0,0 +1,101 @@ +--- +title: "Using the SDK" +description: "This is a guide on using the RowBoat Python SDK as an alternative to the [RowBoat HTTP API](/using_the_api) to power conversations with the assistant created in Studio." +icon: "toolbox" +--- + +## Prerequisites +- ``` pip install rowboat ``` +- [Deploy your assistant to production](/using_the_api/#deploy-your-assistant-to-production-on-studio) +- [Obtain your `` and ``](/using_the_api/#obtain-api-key-and-project-id) + +### API Host +- For the open source installation, the `` is [http://localhost:3000](http://localhost:3000) +- When using the hosted app, the `` is [https://app.rowboatlabs.com](https://app.rowboatlabs.com) + +## Usage + +### Basic Usage with StatefulChat + +The easiest way to interact with Rowboat is using the `StatefulChat` class, which maintains conversation state automatically: + +```python +from rowboat import Client, StatefulChat + +# Initialize the client +client = Client( + host="", + project_id="", + api_key="" +) + +# Create a stateful chat session +chat = StatefulChat(client) + +# Have a conversation +response = chat.run("What is the capital of France?") +print(response) +# The capital of France is Paris. + +# Continue the conversation - the context is maintained automatically +response = chat.run("What other major cities are in that country?") +print(response) +# Other major cities in France include Lyon, Marseille, Toulouse, and Nice. + +response = chat.run("What's the population of the first city you mentioned?") +print(response) +# Lyon has a population of approximately 513,000 in the city proper. +``` + +### Advanced Usage + +#### Using a specific workflow + +You can specify a workflow ID to use a particular conversation configuration: + +```python +chat = StatefulChat( + client, + workflow_id="" +) +``` + +#### Using a test profile + +You can specify a test profile ID to use a specific test configuration: + +```python +chat = StatefulChat( + client, + test_profile_id="" +) +``` + +### Low-Level Usage + +For more control over the conversation, you can use the `Client` class directly: + +```python +from rowboat.schema import UserMessage + +# Initialize the client +client = Client( + host="", + project_id="", + api_key="" +) + +# Create messages +messages = [ + UserMessage(role='user', content="Hello, how are you?") +] + +# Get response +response = client.chat(messages=messages) +print(response.messages[-1].content) + +# For subsequent messages, you need to manage the message history and state manually +messages.extend(response.messages) +messages.append(UserMessage(role='user', content="What's your name?")) +response = client.chat(messages=messages, state=response.state) +``` \ No newline at end of file diff --git a/apps/docs/docs/create_agents.mdx b/apps/docs/docs/create_agents.mdx new file mode 100644 index 000000000..72ee7853d --- /dev/null +++ b/apps/docs/docs/create_agents.mdx @@ -0,0 +1,14 @@ +## Create the set of initial agents +Copilot can set up agents for you from scratch. + +### Instruct copilot +First, tell it about the initial set of agents that make up your assistant. + +![Agent Config](img/create-agents-delivery.png) + +Using copilot to create your initial set of agents helps you leverage best practices in formatting agent instructions and connecting agents to each other as a graph, all of which have been baked into copilot. + +### Inspect the agents +Once you apply changes, inspect the agents to see how copilot has built them. Specifically, note the Instructions, and Examples in each agent. + +![Agent Config](img/agent-instruction.png) diff --git a/apps/docs/docs/data_sources.mdx b/apps/docs/docs/data_sources.mdx new file mode 100644 index 000000000..fcc39fe6d --- /dev/null +++ b/apps/docs/docs/data_sources.mdx @@ -0,0 +1 @@ +Coming soon. \ No newline at end of file diff --git a/apps/docs/docs/development/contribution-guide.mdx b/apps/docs/docs/development/contribution-guide.mdx new file mode 100644 index 000000000..ce65e4bd1 --- /dev/null +++ b/apps/docs/docs/development/contribution-guide.mdx @@ -0,0 +1,59 @@ +--- +title: "Contribution Guide" +description: "Learn how to contribute to the Rowboat project and help improve our platform." +icon: "github" +--- + +# Join the Rowboat Voyage + +We're building Rowboat as an open-source, community-powered platform β€” and we'd love for you to hop aboard! Whether you're fixing typos, suggesting a new tool integration, or designing your dream multi-agent workflow, your contributions are valuable and welcome. + + + Contribution guide hero image showing community collaboration + + +--- + +## How You Can Contribute + + +- **Tackle Open Issues** + Browse our [GitHub Issues](https://github.com/rowboatlabs/rowboat/issues) for tags like `good first issue`, `help wanted`, or `bug` to find a spot that fits your skillset. + +- **Join the Community** + Our [Discord](https://discord.gg/PCkH9TWC) is the go-to hub for brainstorming, feedback, and finding contributors for bigger efforts. + +- **Propose Something New** + Have a new tool integration idea or found a bug? Open an issue and let’s discuss it! + + +--- + +## Contribution Workflow & Best Practices + +Whether it's your first contribution or your fiftieth, here's what a great contribution looks like: + +| Step / Tip | Description | +|-------------------------------|-----------------------------------------------------------------------------------------------| +| **1. Fork the Repository** | Create a personal copy of [rowboatlabs/rowboat](https://github.com/rowboatlabs/rowboat) to start contributing. | +| **2. Create a New Branch** | Use a descriptive name like `fix-tool-crash` or `feature-new-mcp`. Avoid committing to `main`. | +| **3. Make Your Changes** | Focus your PR on a single issue or feature to keep things clean and reviewable. | +| **4. Write Tests if Needed** | If you change logic, add relevant tests so your contribution is future-proof. | +| **5. Run Tests & Lint Locally**| Make sure your branch passes all tests and code quality checks before pushing. | +| **6. Document or Demo It** | Add helpful context: screenshots, example scripts, or a short video/gif to demonstrate your changes. | +| **7. Submit a Pull Request** | Open a PR with a clear description of your changes and any context reviewers should know. | +| **8. Collaborate on Feedback** | Our maintainers may leave comments β€” it’s all part of the process. Let’s improve it together. | +| **9. Don’t Be Shy to Follow Up** | Feel free to ping the PR/issue if you're waiting on feedback. We appreciate polite nudges. | +| **10. Celebrate the Merge!** | You just made Rowboat better. Thanks for contributing πŸš€ | + +If you're fixing typos, spacing, or small tweaks β€” try bundling those into a related PR instead of sending them standalone. It helps keep reviews focused. + + + +--- + +## Come Build With Us + +We believe great ideas come from the community β€” and that means **you**. Whether you're an engineer, designer, AI tinkerer, or curious beginner, there’s room on this boat for everyone. + +Let’s build the future of AI workflows β€” together. 🫢 diff --git a/apps/docs/docs/development/roadmap.mdx b/apps/docs/docs/development/roadmap.mdx new file mode 100644 index 000000000..6248e5673 --- /dev/null +++ b/apps/docs/docs/development/roadmap.mdx @@ -0,0 +1,7 @@ +--- +icon: "road" +--- + +# Roadmap + +Explore the future development plans and upcoming features for Rowboat. \ No newline at end of file diff --git a/apps/docs/docs/getting-started/introduction.mdx b/apps/docs/docs/getting-started/introduction.mdx new file mode 100644 index 000000000..f2e138b82 --- /dev/null +++ b/apps/docs/docs/getting-started/introduction.mdx @@ -0,0 +1,78 @@ +--- +title: "Introduction" +description: "Welcome to the official Rowboat documentation! Rowboat is a low-code AI IDE to build MCP tools connected multi-agent assistants. Rowboat copilot builds the agents for you based on your requirements with the option do everything manually as well." +icon: "book-open" +--- + + + Intro Video + + +## What is RowBoat? +**RowBoat is a state-of-art platform to build multi-agent AI systems in a visual interface, with the help of a copilot.** + +RowBoat enables you to build, manage and deploy user-facing assistants. An assistant is made up of multiple agents, each having access to a set of tools and working together to interact with the user as a single assistant. You can connect any MCP tools to the agents. + +For example, you can build a *credit card assistant*, where each agent handles a workflow such as *outstanding payments*, *balance inquiries* and *transaction disputes*. You can equip agents with tools to carry out tasks such as *fetching payment options*, *checking outstanding balance* and *updating user information*. The assistant would help your end-users their credit card-related needs without having to talk to a human agent on your end. + +--- + +## How RowBoat works + +### RowBoat Studio +RowBoat Studio lets you create AI agents in minutes, using a visual interface and plain language. +There are key components that you will work with: +- Agents +- Playground +- Copilot + + + + Learn about Rowboat Studio and key concepts used in building assistants + + + +### RowBoat Chat API & SDK +- [RowBoat Chat API](/docs/api-sdk/using_the_api) is a stateless HTTP API to interface with the assistant created on RowBoat Studio. You can use the API to drive end-user facing conversations in your app or website. +- [RowBoat Chat SDK](/docs/api-sdk/using_the_sdk) is a simple SDK (currently available in Python) which wraps the HTTP API under the hood. It offers both stateful and stateless (OpenAI-style) implementations. + +--- + +## Why RowBoat? +Rowboat is the fastest way to build and deploy MCP connected multi-agents + + + + Use plain language and a powerful visual interface to design and orchestrate multi-agent assistants with ease. + + + + Add tools and connect to MCP servers in just minutes β€” no complex setup required. + + + + Accelerate development with battle-tested tooling tailored for building production-ready, multi-agent AI systems. + + + +--- + +## Contributing +Want to contribute to Rowboat? Please consider checking out our [Contribution Guide](/docs/development/contribution-guide) + +Star us on github! + + +## Community +Need help using Rowboat? Join our community! + +Join our growing discord community and interact with hundreds of developer using Rowboat! + diff --git a/apps/docs/docs/getting-started/license.mdx b/apps/docs/docs/getting-started/license.mdx new file mode 100644 index 000000000..3e21a84e2 --- /dev/null +++ b/apps/docs/docs/getting-started/license.mdx @@ -0,0 +1,212 @@ +--- +title: "License" +icon: "file" +mode: "center" +# url: "https://github.com/rowboatlabs/rowboat/blob/main/LICENSE" ## An alternate display we could use +--- + +RowBoat is available under the [Apache 2.0 License](https://github.com/rowboatlabs/rowboat/blob/main/LICENSE): + +---- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2024] [RowBoat Labs] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/docs/docs/getting-started/quickstart.mdx b/apps/docs/docs/getting-started/quickstart.mdx new file mode 100644 index 000000000..6350830f5 --- /dev/null +++ b/apps/docs/docs/getting-started/quickstart.mdx @@ -0,0 +1,94 @@ +--- +title: "Quickstart" +description: "guide to getting started with rowboat" +icon: "rocket" +--- +--- +# Cloud Setup + +Using the open-source version of Rowboat requires more technical skill to set up and navigate. For the smoothest experience, we recommend using our [hosted version](https://dev.rowboatlabs.com/) + +--- + +# Local Setup + +Pre-requisite: Ensure Docker is installed on your machine. You'll also need an OpenAI account and API key to use the Copilot and agents. + + + + + Export your OpenAI API key in your terminal: + + ```bash + export OPENAI_API_KEY=your-openai-api-key + ``` + + + + To use external tools and triggers, export your Composio API key: + + ```bash + export COMPOSIO_API_KEY=your-composio-api-key + export COMPOSIO_TRIGGERS_WEBHOOK_SECRET=your-webhook-secret + ``` + + For more detailed setup instructions, see the [Triggers](/docs/using-rowboat/triggers#local-setup) page. + + + + Clone the Rowboat repository and start the app using Docker: + + ```bash + git clone git@github.com:rowboatlabs/rowboat.git + cd rowboat + docker-compose up --build + ``` + + + + Once Docker is running, open your browser and go to: + + [http://localhost:3000](http://localhost:3000) + + + + +See the [Using custom LLM providers](#using-custom-llm-providers) section below for using custom providers like OpenRouter and LiteLLM. + +--- + +## Demo +{/* (would be better to change this to a Getiing Started Tutorial) */} + +#### Create a multi-agent assistant with MCP tools by chatting with Rowboat +[![Screenshot 2025-04-23 at 00 25 31](https://github.com/user-attachments/assets/c8a41622-8e0e-459f-becb-767503489866)](https://youtu.be/YRTCw9UHRbU) + +--- + +## Integrate with Rowboat agents + +There are 2 ways to integrate with the agents you create in Rowboat + + + + + Guide on using the HTTP API + + + + Guide on using the Python SDK + + + + +--- + +## Using custom LLM providers +By default, Rowboat uses OpenAI LLMs (gpt-4o, gpt-4.1, etc.) for both agents and copilot, when you export your OPENAI_API_KEY. + +However, you can also configure custom LLM providers (e.g. LiteLLM, OpenRouter) to use any of the hundreds of available LLMs beyond OpenAI, such as Claude, DeepSeek, Ollama LLMs and so on. + +Check out our page on customising + + Learn more about customising your Rowboat experience here + diff --git a/apps/docs/docs/graph.mdx b/apps/docs/docs/graph.mdx new file mode 100644 index 000000000..c5d47e951 --- /dev/null +++ b/apps/docs/docs/graph.mdx @@ -0,0 +1,23 @@ +# Graph-based Framework + +## Overview + +- Multi-agent systems are popularly represented as graphs, where each agent is a node in the graph. +- In RowBoat, agents are connected to each other as Directed Acyclic Graphs (DAG). +- The graph is also called a workflow, which defines agents, tools, and their connections. +- Since the graph is directed, the control of conversation flows from "parent" agents to their "children" agents +- Every agent is responsible for carrying out a specific part of the workflow, which can involve conversing with the user and / or carrying out tasks such as directing the conversation to other agents. +- [Langgraph](https://www.langchain.com/langgraph) and [Swarm](https://github.com/openai/swarm) are examples of open-source frameworks used to define multi-agent graphs. RowBoat currently supports a Swarm implementation and will extend to Langgraph too in the future. + +## Control Passing + +- While defining the workflow, an agent is designated as the Start agent, to which the first turn of chat will be directed. Typically the Start agent is responsible for triaging the user's query at a high-level and passing control to relevant specific agents which can address the user's needs. +- In any turn of chat, the agent currently in control of the chat has one of 3 options: a) respond to the user (or put out tool calls), b) transfer the chat to any of its children agents or c) transfer the chat back to its parent agent. +- Agents use internal tool calls to transfer the chat to other agents. +- Thus, control passing is achieved by allowing agents to decide flow of control autonomously. +- To the user, the assistant will appear as a unified system, while agents work under the hood. + +## Pipelines + +- RowBoat also has the concept of pipelines - specialized agents invoked sequentially after an agent in the graph has produced a user-facing response. +- E.g. a pipeline with a post processing agent and a guardrail agent will ensure that every response is post processed and guardrailed for appropriateness before presenting it to the user. diff --git a/apps/docs/docs/hosted_setup.mdx b/apps/docs/docs/hosted_setup.mdx new file mode 100644 index 000000000..a564f1e93 --- /dev/null +++ b/apps/docs/docs/hosted_setup.mdx @@ -0,0 +1,156 @@ +# Using the Hosted App + +- This is the developers guide to self-hosting the open-source version of RowBoat. +- Please see our [Introduction](/) page before referring to this guide. +- For direct installation steps, please head to the README of RowBoat's Github repo: [@rowboatlabs/rowboat](https://github.com/rowboatlabs/rowboat/). This page provides more context about the installation process and the different components involved. + +## Overview + +RowBoat's codebase has three main components: + +| Component | Description | +|--------------|---------------| +| **Agents** | Python framework responsible for carrying out multi-agent conversations | +| **Copilot** | Python framework powering the copilot in RowBoat Studio | +| **RowBoat** | Frontend and backend services to power RowBoat Studio and Chat APIs | + +These components are structured as separate services, each containerized with Docker. Running `docker-compose up --build` enables you to use the Studio in your browser, as well as stands up the APIs and SDK. + +## Prerequisites +All of these prerequistes have open-source or free versions. + +| Prerequisite | Description | +|--------------|---------------| +| **Docker** | Bundles and builds all services | +| **OpenAI API Key** | Agents and Copilot services are powered by OpenAI LLMs | +| **MongoDB** | Stores workflow versions, chats and RAG embeddings | +| **Auth0 Account** | Handles user authentication and identity management for Studio | + +Refer to our [Github Readme for Prerequisites](https://github.com/rowboatlabs/rowboat/?tab=readme-ov-file#prerequisites) to set up prerequisites. + +## Setting up + +Refer to our [Github Readme for Local Development](https://github.com/rowboatlabs/rowboat/?tab=readme-ov-file#local-development-setup) to set up Studio, Chat API and SDK via `docker-compose`. + +### Testing Studio + +1. Once you are set up, you should be able to login to the Studio (default local URL: [http://localhost:3000](http://localhost:8000)) via Auth0's login options (Gmail, Github etc.) +
+ +2. Once in Studio, create a new blank project or use one of the example templates: +![Create Project](img/project-page.png) +
+ +3. Use the copilot to help you build agents: +![Use Copilot](img/use-copilot.png) +
+ +4. Ensure that the correct agent is set as the "start agent": +![Set Start Agent](img/start-agent.png) +
+ +5. Test out a chat in the playground to verify the agents' behavior: +![Testing Chat](img/testing-chat.png) +
+ +### Testing the Chat API + +You can use the API directly at [http://localhost:3000/api/v1/](http://localhost:3000/api/v1/) +- Project ID is available in the URL of the project page +- API Key can be generated from the project config page at `/projects//config` + +Below is an example request and response. Modify the user message in the request, based on your example project. + +**Request:** + +```bash +curl --location 'http://localhost:3000/api/v1//chat' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer ' \ +--data '{ + "messages": [ + { + "role": "user", + "content": "What is my pending payment amount?" + } + ] +}' +``` +**Response:** +The last message in `messages` is either a user-facing response or a tool call by the assistant. + +```json +{ + "messages": [ + { + "sender": "Credit Card Hub", + "role": "assistant", + "response_type": "internal", + "content": null, + "created_at": "2025-02-01T06:55:47.843909", + "current_turn": true, + "tool_calls": [ + { + "function": { + "arguments": "{\"args\":\"\",\"kwargs\":\"\"}", + // Internal tool calls are used to transfer between agents + "name": "transfer_to_outstanding_payment" + }, + "id": "call_7jGpwpVvzhZFOyRgxHFkdOdU", + "type": "function" + } + ] + }, + { + "tool_name": "transfer_to_outstanding_payment", + "role": "tool", + "content": "{\"assistant\": \"Outstanding Payment\"}", + "tool_call_id": "call_7jGpwpVvzhZFOyRgxHFkdOdU" + }, + { + "sender": "Outstanding Payment", + "role": "assistant", + // Response is not user-facing, to enable further post processing + "response_type": "internal", + "content": "Sure, could you provide the last four digits of your card or your registered mobile number so I can look up your pending payment amount?", + "created_at": "2025-02-01T06:55:49.648008", + "current_turn": true + }, + { + "sender": "Outstanding Payment >> Post process", + "role": "assistant", + // Response is user-facing + "response_type": "external", + "content": "Sure, please provide the last four digits of your card or your registered mobile number so I can check your pending payment amount.", + "created_at": "2025-02-01T06:55:49.648008", + "current_turn": true + } + ], + "state": { + // .. state data + } +} +``` + +### Testing the Python Chat SDK + +```bash +pip install rowboat +``` + +Modify the user message in `messages`, based on your example project. + +```python +from rowboat import Client + +client = Client( + host="http://localhost:3000", + project_id="", + api_key="" # Generate this from /projects//config +) + +# Simple chat interaction +messages = [{"role": "user", "content": "What is my pending payment amount?"}] +response_messages, state = client.chat(messages=messages) +``` +The last message in `response_messages` is either a user-facing response or a tool call by the assistant. \ No newline at end of file diff --git a/apps/docs/docs/img/Intro-Video.gif b/apps/docs/docs/img/Intro-Video.gif new file mode 100644 index 000000000..d4eb387ef Binary files /dev/null and b/apps/docs/docs/img/Intro-Video.gif differ diff --git a/apps/docs/docs/img/contribution-guide-hero.png b/apps/docs/docs/img/contribution-guide-hero.png new file mode 100644 index 000000000..705f1a1d7 Binary files /dev/null and b/apps/docs/docs/img/contribution-guide-hero.png differ diff --git a/apps/docs/docs/img/conversations-inside-run.png b/apps/docs/docs/img/conversations-inside-run.png new file mode 100644 index 000000000..540474a6b Binary files /dev/null and b/apps/docs/docs/img/conversations-inside-run.png differ diff --git a/apps/docs/docs/img/conversations-ui.png b/apps/docs/docs/img/conversations-ui.png new file mode 100644 index 000000000..1ab34a855 Binary files /dev/null and b/apps/docs/docs/img/conversations-ui.png differ diff --git a/apps/docs/docs/img/jobs-inside-run.png b/apps/docs/docs/img/jobs-inside-run.png new file mode 100644 index 000000000..ed7d39a81 Binary files /dev/null and b/apps/docs/docs/img/jobs-inside-run.png differ diff --git a/apps/docs/docs/img/jobs-ui.png b/apps/docs/docs/img/jobs-ui.png new file mode 100644 index 000000000..9dd2662fa Binary files /dev/null and b/apps/docs/docs/img/jobs-ui.png differ diff --git a/apps/docs/docs/img/rag-adding-data.png b/apps/docs/docs/img/rag-adding-data.png new file mode 100644 index 000000000..675d77a6c Binary files /dev/null and b/apps/docs/docs/img/rag-adding-data.png differ diff --git a/apps/docs/docs/img/tools-ui.png b/apps/docs/docs/img/tools-ui.png new file mode 100644 index 000000000..b9283d22b Binary files /dev/null and b/apps/docs/docs/img/tools-ui.png differ diff --git a/apps/docs/docs/img/triggers-external-ui.png b/apps/docs/docs/img/triggers-external-ui.png new file mode 100644 index 000000000..e3057ae1d Binary files /dev/null and b/apps/docs/docs/img/triggers-external-ui.png differ diff --git a/apps/docs/docs/img/triggers-onetime-ui.png b/apps/docs/docs/img/triggers-onetime-ui.png new file mode 100644 index 000000000..d38ca558f Binary files /dev/null and b/apps/docs/docs/img/triggers-onetime-ui.png differ diff --git a/apps/docs/docs/img/triggers-recurring-ui.png b/apps/docs/docs/img/triggers-recurring-ui.png new file mode 100644 index 000000000..6fd5ee26c Binary files /dev/null and b/apps/docs/docs/img/triggers-recurring-ui.png differ diff --git a/apps/docs/docs/oss_installation.mdx b/apps/docs/docs/oss_installation.mdx new file mode 100644 index 000000000..b26c53884 --- /dev/null +++ b/apps/docs/docs/oss_installation.mdx @@ -0,0 +1,32 @@ +# Open Source Installation + +- This is the developers guide to self-hosting the open-source version of RowBoat. To get started with the hosted app, please see [Using the Hosted App](/hosted_setup) +- Please see our [Introduction](/) page before referring to this guide. +- For direct installation steps, please head to the README of RowBoat's Github repo: [@rowboatlabs/rowboat](https://github.com/rowboatlabs/rowboat/). This page provides more context about the installation process and the different components involved. + +## Overview + +RowBoat's codebase has three main components: + +| Component | Description | +|--------------|---------------| +| **Agents** | Python framework responsible for carrying out multi-agent conversations | +| **Copilot** | Python framework powering the copilot in RowBoat Studio | +| **RowBoat** | Frontend and backend services to power RowBoat Studio and Chat APIs | + +These components are structured as separate services, each containerized with Docker. Running `docker-compose up --build` enables you to use the Studio in your browser, as well as stands up the APIs and SDK. + +## Prerequisites +All of these prerequisites have open-source or free versions. + +| Prerequisite | Description | +|--------------|---------------| +| **Docker** | Bundles and builds all services | +| **OpenAI API Key** | Agents and Copilot services are powered by OpenAI LLMs | +| **MongoDB** | Stores workflow versions, chats and RAG embeddings | + +Refer to our [Github Readme for Prerequisites](https://github.com/rowboatlabs/rowboat/?tab=readme-ov-file#prerequisites) to set up prerequisites. + +## Setting up + +Refer to our [Github Readme for Local Development](https://github.com/rowboatlabs/rowboat/?tab=readme-ov-file#local-development-setup) to set up Studio, Chat API and SDK via `docker-compose`. \ No newline at end of file diff --git a/apps/docs/docs/playground.mdx b/apps/docs/docs/playground.mdx new file mode 100644 index 000000000..3f1f0590c --- /dev/null +++ b/apps/docs/docs/playground.mdx @@ -0,0 +1,7 @@ +## Try an example chat in the playground + +### Chat with the assistant + +The playground is intended to test out the assistant as you build it. The User and Assistant messages represent the conversation that your end-user will have if your assistant is deployed in production. The playground also has debug elements which show the flow of control between different agents in your system, as well as which agent finally responded to the user. + +![Try Chat](img/chat-delivery.png) \ No newline at end of file diff --git a/apps/docs/docs/prompts.mdx b/apps/docs/docs/prompts.mdx new file mode 100644 index 000000000..2552cc49f --- /dev/null +++ b/apps/docs/docs/prompts.mdx @@ -0,0 +1,5 @@ +# Prompts + +- Prompts are reusable pieces of agent instructions in Studio. +- Prompts can be defined once and reused across multiple agents. +- Common examples of prompts are style prompts which indicate brand voice and structured output prompts which specify a format for the agent to provide its output in (e.g. ReAct) \ No newline at end of file diff --git a/apps/docs/docs/simulate.mdx b/apps/docs/docs/simulate.mdx new file mode 100644 index 000000000..e488d0d5d --- /dev/null +++ b/apps/docs/docs/simulate.mdx @@ -0,0 +1,6 @@ +## Simulate real-world user scenarios +Create a test-bench of real-world scenarios in the simulator. +![Scenarios](img/scenarios.png) + +Run the scenarios as simulated chats betweeen a user (role-played) and the assistant, in the playground. +![Simulation](img/simulate.png) \ No newline at end of file diff --git a/apps/docs/docs/studio_overview.mdx b/apps/docs/docs/studio_overview.mdx new file mode 100644 index 000000000..9467708c9 --- /dev/null +++ b/apps/docs/docs/studio_overview.mdx @@ -0,0 +1,7 @@ +# Building Assistants in Studio +This is a guide to building your first assistant on RowBoat Studio, with examples.
+ +Prerequisite: + +1. **Open Source Users:** Complete the [open-source installation steps](/oss_installation/) to set up RowBoat Studio. +2. **Hosted App Users:** Sign in to [https://app.rowboatlabs.com/](https://app.rowboatlabs.com/) \ No newline at end of file diff --git a/apps/docs/docs/testing.mdx b/apps/docs/docs/testing.mdx new file mode 100644 index 000000000..3ad7f843e --- /dev/null +++ b/apps/docs/docs/testing.mdx @@ -0,0 +1,132 @@ +# Testing Your Setup + +## Testing Studio + +1. Once you are set up, you should be able to login to the Studio via Auth0's login options (Gmail, Github etc.). +- For the open source installation, the URL for Studio is [http://localhost:3000](http://localhost:3000) +- To use our hosted app, the URL for Studio is [https://app.rowboatlabs.com](https://app.rowboatlabs.com/) +
+ +2. Once in Studio, create a new blank project or browse through one of the example projects: +![Create Project](img/project-page.png) +
+ +3. Use the copilot to help you build agents: +![Use Copilot](img/use-copilot.png) +
+ +4. Ensure that the correct agent is set as the "start agent": +![Set Start Agent](img/start-agent.png) +
+ +5. Test out a chat in the playground to verify the agents' behavior: +![Testing Chat](img/testing-chat.png) +
+ +### Testing the Chat API + +- For the open source installation, the `` is [http://localhost:3000](http://localhost:3000) +- When using the hosted app, the `` is [https://app.rowboatlabs.com](https://app.rowboatlabs.com) +- `` is available in the URL of the project page +- API Key can be generated from the project config page at `/projects//config` + +Below is an example request and response. Modify the user message in the request, based on your example project. + +**Request:** + +```bash +curl --location 'http:///api/v1//chat' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Bearer ' \ +--data '{ + "messages": [ + { + "role": "user", + "content": "What is my pending payment amount?" + } + ] +}' +``` +**Response:** +The last message in `messages` is either a user-facing response or a tool call by the assistant. + +```json +{ + "messages": [ + { + "sender": "Credit Card Hub", + "role": "assistant", + "response_type": "internal", + "content": null, + "created_at": "2025-02-01T06:55:47.843909", + "current_turn": true, + "tool_calls": [ + { + "function": { + "arguments": "{\"args\":\"\",\"kwargs\":\"\"}", + // Internal tool calls are used to transfer between agents + "name": "transfer_to_outstanding_payment" + }, + "id": "call_7jGpwpVvzhZFOyRgxHFkdOdU", + "type": "function" + } + ] + }, + { + "tool_name": "transfer_to_outstanding_payment", + "role": "tool", + "content": "{\"assistant\": \"Outstanding Payment\"}", + "tool_call_id": "call_7jGpwpVvzhZFOyRgxHFkdOdU" + }, + { + "sender": "Outstanding Payment", + "role": "assistant", + // Response is not user-facing, to enable further post processing + "response_type": "internal", + "content": "Sure, could you provide the last four digits of your card or your registered mobile number so I can look up your pending payment amount?", + "created_at": "2025-02-01T06:55:49.648008", + "current_turn": true + }, + { + "sender": "Outstanding Payment >> Post process", + "role": "assistant", + // Response is user-facing + "response_type": "external", + "content": "Sure, please provide the last four digits of your card or your registered mobile number so I can check your pending payment amount.", + "created_at": "2025-02-01T06:55:49.648008", + "current_turn": true + } + ], + "state": { + // .. state data + } +} +``` + +### Testing the Python Chat SDK + +- For the open source installation, the `` is [http://localhost:3000](http://localhost:3000) +- When using the hosted app, the `` is [https://app.rowboatlabs.com](https://app.rowboatlabs.com) +- `` is available in the URL of the project page +- API Key can be generated from the project config page at `/projects//config` + +```bash +pip install rowboat +``` + +Modify the user message in `messages`, based on your example project. + +```python +from rowboat import Client + +client = Client( + host="", + project_id="", + api_key="" # Generate this from /projects//config +) + +# Simple chat interaction +messages = [{"role": "user", "content": "What is my pending payment amount?"}] +response_messages, state = client.chat(messages=messages) +``` +The last message in `response_messages` is either a user-facing response or a tool call by the assistant. \ No newline at end of file diff --git a/apps/docs/docs/tools.mdx b/apps/docs/docs/tools.mdx new file mode 100644 index 000000000..162dad365 --- /dev/null +++ b/apps/docs/docs/tools.mdx @@ -0,0 +1,6 @@ +# Tools +- Tools are used to carry out specific tasks such as fetching or updating information. +- Tools can be defined once in RowBoat Studio and reused across different agents. +- RowBoat uses OpenAI style tools with name, description and parameters. +- For the purposes of quick testing in the Playground, RowBoat Studio can mock tool responses based on tool descriptions. +- Developers can easily connect tools to APIs by configuring MCP servers or Webhook URL in Settings. \ No newline at end of file diff --git a/apps/docs/docs/update_agents.mdx b/apps/docs/docs/update_agents.mdx new file mode 100644 index 000000000..10fd437bc --- /dev/null +++ b/apps/docs/docs/update_agents.mdx @@ -0,0 +1,19 @@ +## Update agent behavior + +There are three ways for you to update the agent's behavior: + +### 1. With help of Copilot + +Copilot can help you update agent behavior. It is also aware of the current chat in the playground so you can make references to the current chat while instructing copilot to update agents. + +![Update Agent Behavior](img/update-agent-copilot.png) + +### 2. Using the Generate button + +![Update Agent Behavior](img/update-agent-generate.png) + +### 3. By manually editing the instructions + +You can manually edit the agent instructions anytime. + +![Update Agent Behavior](img/update-agent-manual.png) diff --git a/apps/docs/docs/using-rowboat/agents.mdx b/apps/docs/docs/using-rowboat/agents.mdx new file mode 100644 index 000000000..9e0a65765 --- /dev/null +++ b/apps/docs/docs/using-rowboat/agents.mdx @@ -0,0 +1,141 @@ +--- +title: "Agents" +description: "Learn about creating and configuring individual agents within your multi-agent system" +icon: "robot" +--- + +## Overview + +Agents are the core building blocks of Rowboat's multi-agent system. Each agent carries out a specific part of a conversation, handles tasks via tools, and can collaborate with other agents to orchestrate complex workflows. + +They are powered by LLMs and can: +- Respond to user input +- Trigger tools or APIs +- Pass control to other agents using @mentions +- Fetch or process internal data +- Execute RAG (Retrieval-Augmented Generation) queries +- Participate in sequential pipeline workflows + +--- + +## Agent Types + +Rowboat supports several types of agents, each designed for specific use cases: + +| Name | Purpose | Characteristics | +|------|---------|-----------------| +| **Conversational Agents** (`conversation`) | Primary user-facing agents that interact directly with users and orchestrate workflows. | β€’ Can respond to users and orchestrate workflows
β€’ Typically serve as the start agent (Hub Agent)| +| **Task Agents** (`internal`) | Specialized agents that perform specific tasks without direct user interaction. | β€’ Focused on specific functions
β€’ Return results to parent agents| +| **Pipeline Agents** (`pipeline`) | Sequential workflow execution agents that process data in a chain. | β€’ Execute in sequence within a pipeline
β€’ Cannot transfer to other agents directly| + + +--- + +## Agent Configuration + +Agents are configured through two main tabs in the Rowboat Studio interface: + +### **Instructions Tab** + + +#### Description +A clear description of the agent's role and responsibilities + +#### Instructions +Instructions are the backbone of the agent's behavior. Use the Copilot's structured format for consistency: + +**Recommended Structure:** +``` +## πŸ§‘β€πŸ’Ό Role: +[Clear description of the agent's role] + +## βš™οΈ Steps to Follow: +1. [Step 1] +2. [Step 2] +3. [Step 3] + +## 🎯 Scope: +βœ… In Scope: +- [What the agent should handle] + +❌ Out of Scope: +- [What the agent should NOT handle] + +## πŸ“‹ Guidelines: +βœ”οΈ Dos: +- [Positive behaviors] + +🚫 Don'ts: +- [Negative behaviors] +``` + +#### Examples +These help agents behave correctly in specific situations. Each example can include: +- A sample user message +- The expected agent response +- Any tool calls (if applicable) + +### **Configurations Tab** + +#### Name +Name of the agent + + +#### Behaviour +- **Agent Type**: Choose from `conversation`, `internal`, or `pipeline` +- **Model**: Select the LLM model (GPT-4o, GPT-4o-mini, google/gemini-2.5-flash, etc.) + +#### RAG +- **Add Source**: Connect data sources to enable RAG capabilities for the agent + +--- + +## Creating Your Initial Set of Agents + +Let Copilot bootstrap your agent graph. + +### Instruct Copilot + +Start by telling Copilot what your assistant is meant to do β€” it'll generate an initial set of agents with best-practice instructions, role definitions, and connected agents. + + + Creating agents with Copilot + + +### Inspect the Output + +After applying the suggested agents, take a close look at each one's: +- **Instructions**: Define how the agent behaves +- **Examples**: Guide agent responses and tool use + + + Inspect agent instructions + + +--- + +## Updating Agent Behavior + +There are three ways to update an agent: + +### 1. With Copilot + +Copilot understands the current chat context and can help rewrite or improve an agent's behavior based on how it performed. + + + Update agent using Copilot + + + + +### 2. Manual Edits + +You can always manually edit the agent's instructions. + + + Manually edit agent + + +--- + + diff --git a/apps/docs/docs/using-rowboat/conversations.mdx b/apps/docs/docs/using-rowboat/conversations.mdx new file mode 100644 index 000000000..c755f47d2 --- /dev/null +++ b/apps/docs/docs/using-rowboat/conversations.mdx @@ -0,0 +1,52 @@ +--- +title: "Conversations" +description: "View and manage all conversations with your Rowboat agents" +icon: "list-check" +--- + +## Overview + +The Conversations page in Rowboat shows you all the interactions between users and your agents. Here you can monitor conversations, view detailed message exchanges, and understand how your agents are performing. + + + Conversations page UI showing list of conversations + + +## What You'll See + +The Conversations page displays a list of all conversations organized by time: + +- **Today**: Recent conversations from today +- **This week**: Conversations from the current week +- **This month**: Conversations from the current month +- **Older**: Conversations from previous months + +Each conversation shows: +- **Conversation ID**: Unique identifier for the conversation +- **Created time**: When the conversation started +- **Reason**: What triggered the conversation (chat or job) + +## Viewing Conversation Details +Click on any conversation to see the detailed view with all the message exchanges: + + + Conversation details showing expanded view of a conversation with turns and messages + + +**Conversation Metadata**: Shows the Conversation ID, creation time, and last update time. + +**Workflow**: Shows the workflow JSON + +**Turns**: Each conversation is made up of turns, where: +- **Turn #1, #2, etc.**: Numbered sequence of interactions +- **Reason badge**: Shows why each turn happened (chat, API, job, etc.) +- **Timestamp**: When each turn occurred +- **Input messages**: What was sent to your agents +- **Output messages**: What your agents responded with + +### Turn Details + +Each turn displays: +- **Input**: The messages sent to your agents (user messages, system messages) +- **Output**: The responses from your agents +- **Error information**: Any issues that occurred during processing diff --git a/apps/docs/docs/using-rowboat/customise/custom-llms.mdx b/apps/docs/docs/using-rowboat/customise/custom-llms.mdx new file mode 100644 index 000000000..dc87dc2f2 --- /dev/null +++ b/apps/docs/docs/using-rowboat/customise/custom-llms.mdx @@ -0,0 +1,53 @@ +--- +title: "Custom LLMs" +description: "How to use and configure custom LLMs in Rowboat." + +--- + + This is currently only possible in the self hosted version of Rowboat + +## Using custom LLM providers + +By default, Rowboat uses OpenAI LLMs (gpt-4o, gpt-4.1, etc.) for both agents and copilot, when you export your OPENAI_API_KEY. + +However, you can also configure custom LLM providers (e.g. LiteLLM, OpenRouter) to use any of the hundreds of available LLMs beyond OpenAI, such as Claude, DeepSeek, Ollama LLMs and so on. + + + + Configure your environment variables to point to your preferred LLM backend. Example using LiteLLM: + + ```bash + export PROVIDER_BASE_URL=http://host.docker.internal:4000/ + export PROVIDER_API_KEY=sk-1234 + ``` + + Rowboat uses gpt-4.1 as the default model for agents and copilot. You can override these: + + ```bash + export PROVIDER_DEFAULT_MODEL=claude-3-7-sonnet-latest + export PROVIDER_COPILOT_MODEL=gpt-4o + ``` + + **Notes:** + - Copilot is optimized for gpt-4o/gpt-4.1. We strongly recommend using these models for best results. + - You can use different models for the copilot and each agent, but all must be from the same provider (e.g., LiteLLM). + - Rowboat is provider-agnostic β€” any backend implementing the OpenAI messages format should work. + - OpenAI-specific tools (like web_search) will not function with non-OpenAI providers. Remove such tools to avoid errors. + + + + Clone the Rowboat repo and spin it up locally: + + ```bash + git clone git@github.com:rowboatlabs/rowboat.git + cd rowboat + docker-compose up --build + ``` + + + + Once Docker is running, navigate to: + + [http://localhost:3000](http://localhost:3000) + + diff --git a/apps/docs/docs/using-rowboat/jobs.mdx b/apps/docs/docs/using-rowboat/jobs.mdx new file mode 100644 index 000000000..5b3e14dfd --- /dev/null +++ b/apps/docs/docs/using-rowboat/jobs.mdx @@ -0,0 +1,45 @@ +--- +title: "Jobs" +description: "Monitor and inspect all your trigger executions and job runs" +icon: "message" +--- + +## Overview + +The Jobs page in Rowboat provides a comprehensive view of all your automated job executions. Here you can monitor the status of your triggers, inspect what happened during each run, and troubleshoot any issues that may have occurred. + + + Jobs page showing list of all job runs with status indicators + + +## What You'll See + +The Jobs page displays a list of all job runs from your triggers, including: + +- **External trigger executions** from webhook events +- **One-time trigger runs** from scheduled jobs +- **Recurring trigger executions** from cron-based schedules + +Each job run displays the following key information: +- **Job ID**: Unique identifier for the job run +- **Status**: Indicates if the job succeeded, failed, or is in progress +- **Reason**: The trigger or cause for the job (e.g., external trigger, scheduled, cron) +- **Created Time**: When the job was executed + +## Viewing Job Details + +### Expand a Job Run + +Click on any job run to expand it and see detailed information about what happened during execution: + + + Job run details showing expanded information for a specific job + + +**Basic job details**: Job ID, Status, creation time, Updated time, Conversation ID and Turn ID. By clicking on the Conversation ID, you can view more in-depth details about the run. + +**Job Reason**: Why the job triggered - either external trigger, scheduled, or cron. + +**Job Input**: The input data sent to your assistant. + +**Job Output**: The final output produced by your agents. \ No newline at end of file diff --git a/apps/docs/docs/using-rowboat/rag.mdx b/apps/docs/docs/using-rowboat/rag.mdx new file mode 100644 index 000000000..772d034d5 --- /dev/null +++ b/apps/docs/docs/using-rowboat/rag.mdx @@ -0,0 +1,68 @@ +--- +title: "RAG (Data)" +description: "How to use our inbuilt RAG" +icon: "database" +--- + +# Using RAG in Rowboat + +Rowboat provides multiple ways to enhance your agents' context with Retrieval-Augmented Generation (RAG). This guide will help you set up and use each RAG feature. + +RAG is called "Data" on the build view in the Rowboat UI. + +--- + +## Types of RAG + +| RAG Type | Description | Configuration Required | +|----------|-------------|------------------------| +| **Text RAG** | Process and reason over text content directly | No configuration needed | +| **File Uploads** | Upload PDF files directly from your device | No configuration needed | +| **URL Scraping** | Scrape content from web URLs using Firecrawl | Requires API key setup | + + URL Scraping does not require any setup in the managed version of Rowboat. + + + Adding data sources for RAG in Rowboat + + + +## RAG Features + +### 1. Text RAG + +Process and reason over text content directly + + +### 2. File Uploads + +- Upload PDF files directly from your device +- **Open Source Version**: Files are stored locally on your machine +- **Managed Version**: Files are stored in cloud S3 storage +- Files are parsed using OpenAI by default + + You can also use Google's Gemini model for parsing as it is better at parsing larger files. + +#### 2.1 Using Gemini for File Parsing + +To use Google's Gemini model for parsing uploaded PDFs, set the following variables: + +```bash +# Enable Gemini for file parsing +export USE_GEMINI_FILE_PARSING=true +export GOOGLE_API_KEY=your_google_api_key +``` + + +### 3. URL Scraping + +Rowboat uses Firecrawl for URL scraping. You can have a maximum of 100 URLs. + +**Open Source Version**: To enable URL scraping, set the following variables: + +```bash +export USE_RAG_SCRAPING=true +export FIRECRAWL_API_KEY=your_firecrawl_api_key +``` + +**Managed Version**: No configuration required - URL scraping is handled automatically. diff --git a/apps/docs/docs/using-rowboat/rowboat-studio.mdx b/apps/docs/docs/using-rowboat/rowboat-studio.mdx new file mode 100644 index 000000000..edf74fb3e --- /dev/null +++ b/apps/docs/docs/using-rowboat/rowboat-studio.mdx @@ -0,0 +1,60 @@ +--- +title: "Rowboat Studio" +description: "Visual interface to build, test, and deploy multi-agent AI assistants using plain language" +icon: "puzzle-piece" +--- + + +## Overview + +**Rowboat Studio** is your visual interface for building AI assistants β€” powered by agents, tools, and workflows β€” using plain language and minimal setup. It brings the process of creating multi-agent systems down to just a few clicks. + +Workflows created within Rowboat are known as **assistants**, and each assistant is composed of: +- One or more **agents** +- Attached **tools** and **MCP servers** + +Once built, assistants can be tested live in the **playground** and deployed in real-world products using the [API](/docs/api-sdk/using_the_api) or [SDK](/docs/api-sdk//using_the_sdk). + +--- + +## Key Components + +Here’s what you’ll interact with in Studio: + +| Component | Description | Highlights | +|------------|-------------|------------| +| **Agent** | Core building blocks of your assistant.
Each agent handles a specific part of the conversation and performs tasks using tools and instructions. | β€’ Define behavior in plain language
β€’ Connect agents into a graph
β€’ Attach tools and RAG sources | +| **Playground** | Interactive testbed for conversations.
Lets you simulate end-user chats with your assistant and inspect agent behavior in real time. | β€’ Real-time feedback and debugging
β€’ See tool calls and agent handoffs
β€’ Test individual agents or the whole system | +| **Copilot** | Your AI assistant for building assistants.
Copilot creates and updates agents, tools, and instructions based on your plain-English prompts. | β€’ Understands full system context
β€’ Improves agents based on playground chat
β€’ Builds workflows intelligently and fast | + +> **Agents are the heart of every assistant.** Learn more about how they work in the Agents page. + + + Learn about creating and configuring individual agents within your multi-agent system + +--- + +## Building in Rowboat + + + + Use plain language to tell Copilot what you want your assistant to do. Copilot will auto-generate the agents, instructions, and tools that form the base of your assistant. + + + + Inspect the created agents β€” especially their instructions and examples β€” and refine or approve them before moving forward. + + + + Integrate external services, tools, and backend logic into your agents using Rowboat's modular system. Tools are tied to agents and triggered through instructions. + + + + Use the chat playground to simulate real-world conversations. You’ll see which agent takes control, what tools are triggered, and how your assistant flows. + + + + Assistants can be deployed into production using the **Rowboat Chat API** or the **Rowboat Chat SDK**. Both support stateless and stateful conversation flows. + + + diff --git a/apps/docs/docs/using-rowboat/tools.mdx b/apps/docs/docs/using-rowboat/tools.mdx new file mode 100644 index 000000000..fec926096 --- /dev/null +++ b/apps/docs/docs/using-rowboat/tools.mdx @@ -0,0 +1,48 @@ +--- +title: "Tools" +description: "Add and configure tools for your agents to interact with external services" +icon: "wrench" +--- + +## Overview + +The Tools page in Rowboat lets you add and configure tools that your agents can use to interact with external services, APIs, and systems. Tools enable your agents to perform real-world actions like sending emails, managing calendars, or processing payments. + + + Screenshot of the Tools UI in Rowboat + + +## Tool Types + +| Tool Type | Description | Use Case | Availability | +|-----------|-------------|----------|--------------| +| **Library Tools** | Pre-built integrations with popular services | Quick setup, no configuration needed | Managed and open source | +| **MCP Tools** | Custom tools from MCP servers | Custom functionality, specialized APIs | Managed and open source | +| **Webhook Tools** | HTTP endpoints for custom integrations | Your own systems, custom workflows | Open source only | + + +## Library (Composio Tools) + +- Browse a library of 500+ toolkits from popular services +- With 3000+ tools to choose from! +- Click on a service to see available tools and add them to your workflow +- Users must create a Composio account and add their API key +- Tools require authorization to work properly + +Users can visit [Composio's toolkit documentation](https://docs.composio.dev/toolkits/introduction) for a deep dive into all the tools available. + +## Custom MCP Servers + +- Add your own MCP (Model Context Protocol) servers +- Connect to custom tools and APIs you've built +- Configure server URLs and authentication +- Import tools from your MCP servers + +## Webhook + +Webhook tools are only available in the open source (local) version of Rowboat. + +- Create custom webhook tools +- Configure HTTP endpoints for your agents to call +- Set up custom authentication and parameters +- Build integrations with your own systems diff --git a/apps/docs/docs/using-rowboat/triggers.mdx b/apps/docs/docs/using-rowboat/triggers.mdx new file mode 100644 index 000000000..32c81f66d --- /dev/null +++ b/apps/docs/docs/using-rowboat/triggers.mdx @@ -0,0 +1,131 @@ +--- +title: "Triggers" +description: "Learn about setting up automated triggers for your Rowboat agents" +icon: "bolt" +--- + +## Overview + +Triggers in Rowboat are automated mechanisms that activate your agents when specific events occur or conditions are met. They form the foundation of your automated workflow system, enabling your agents to respond to external events, scheduled times, and system conditions without manual intervention. + + + +## Trigger Types + +Rowboat supports three main categories of triggers, each designed for different automation scenarios: + +| Trigger Type | Purpose | Execution | Use Cases | +|--------------|---------|-----------|-----------| +| **External Triggers** | Connect to external services and events | Real-time via webhooks | Slack messages, GitHub events, email processing | +| **One-Time Triggers** | Execute at specific predetermined times | Single execution at set time | Delayed responses, time-sensitive actions | +| **Recurring Triggers** | Execute on repeating schedules | Continuous via cron expressions | Daily reports, periodic maintenance, regular syncs | + +--- + +## External Triggers (Composio Integration) + +External triggers are powered by **Composio** and allow users to use triggers from across 30+ services including Slack, GitHub, Gmail, Notion, Google Calendar, and more. + + + External Triggers UI showing available toolkits and services + + +### Creating External Triggers + +1. **Click New External Trigger**: Start the trigger creation process +2. **Select a Toolkit**: Browse available toolkits or search for specific services +3. **Choose Trigger Type**: Select the specific trigger from available options, click configure +4. **Authenticate**: Complete OAuth2 flow or enter API keys for the selected service (your preferred method) +5. **Configure**: Set up event filters and data mapping if required +6. **Deploy**: Activate the trigger to start listening for events + +### Local Setup + +If you're running the open source version of Rowboat, you'll need to set up external triggers manually. In the managed version, this is all handled automatically for you. + + + + Sign into [Composio](https://composio.dev/) and create a new project for your Rowboat instance. + + + + Go to your project settings and copy the project API key. Export it in your Rowboat environment: + + ```bash + export COMPOSIO_API_KEY=your-composio-api-key + ``` + + + + Use ngrok to expose your local Rowboat instance: + + ```bash + ngrok http 3000 + ``` + + Copy the generated ngrok URL (e.g., `https://a5fe8c0d45b8.ngrok-free.app`). + + + + In Composio, go to Events & Triggers section and set the Trigger Webhook URL to: + + ``` + {ngrok_url}/api/composio/webhook + ``` + + Example: `https://a5fe8c0d45b8.ngrok-free.app/api/composio/webhook` + + + + Copy the Webhook Secret from Composio and export it in Rowboat: + + ```bash + export COMPOSIO_TRIGGERS_WEBHOOK_SECRET=your-webhook-secret + ``` + + + + Restart your Rowboat instance to load the new environment variables. You're now ready to use external triggers! + + + +Make sure your Rowboat assistant is deployed before receiving trigger calls + +--- + +## One-Time Triggers (Scheduled Jobs) + +One-time triggers execute your agents at a specific, predetermined time. They're useful for delayed responses, batch processing, time-sensitive actions, or coordinating with external schedules. + + + One-Time Triggers UI showing scheduled job configuration + + +### Creating One-Time Triggers +1. Set the exact execution time (date and time) +2. Configure the input messages for your agents +3. Deploy to schedule the execution + +--- + +## Recurring Triggers (Cron-based Jobs) + +Recurring triggers execute your agents on a repeating schedule using cron expressions. They're ideal for daily reports, periodic maintenance, regular data syncs, and continuous monitoring tasks. + + + Recurring Triggers UI showing cron-based job configuration + + +### Creating Recurring Triggers +1. Define the cron expression (e.g., `0 9 * * *` for daily at 9 AM) +2. Configure the recurring message structure +3. Enable the trigger to start the recurring schedule + +### Common Cron Patterns +```cron +0 9 * * * # Daily at 9:00 AM +0 8 * * 1 # Every Monday at 8:00 AM +*/15 * * * * # Every 15 minutes +0 0 1 * * # First day of month at midnight +``` + diff --git a/apps/docs/docs/using_rag.mdx b/apps/docs/docs/using_rag.mdx new file mode 100644 index 000000000..52deefadf --- /dev/null +++ b/apps/docs/docs/using_rag.mdx @@ -0,0 +1,103 @@ +# Using RAG in Rowboat + +Rowboat provides multiple ways to enhance your agents' context with Retrieval-Augmented Generation (RAG). This guide will help you set up and use each RAG features. + +## Quick Start + +Text RAG and local file uploads are enabled by default - no configuration needed! Just start using them right away. + +## RAG Features + +### 1. Text RAG +βœ… Enabled by default: + +- Process and reason over text content directly +- No configuration required + +### 2. Local File Uploads +βœ… Enabled by default: + +- Upload PDF files directly from your device +- Files are stored locally +- No configuration required +- Files are parsed using OpenAI by default +- For larger files, we recommend using Gemini models - see section below. + +#### 2.1 Using Gemini for File Parsing +To use Google's Gemini model for parsing uploaded PDFs, set the following variable: + +```bash +# Enable Gemini for file parsing +export USE_GEMINI_FILE_PARSING=true +export GOOGLE_API_KEY=your_google_api_key +``` + +### 3. URL Scraping +Rowboat uses Firecrawl for URL scraping. To enable URL scraping, set the following variables: + +```bash +export USE_RAG_SCRAPING=true +export FIRECRAWL_API_KEY=your_firecrawl_api_key +``` + +## Advanced RAG features + +### 1. File Uploads Backed by S3 +To enable S3 file uploads, set the following variables: + +```bash +# Enable S3 uploads +export USE_RAG_S3_UPLOADS=true + +# S3 Configuration +export AWS_ACCESS_KEY_ID=your_access_key +export AWS_SECRET_ACCESS_KEY=your_secret_key +export RAG_UPLOADS_S3_BUCKET=your_bucket_name +export RAG_UPLOADS_S3_REGION=your_region +``` + +### 2. Changing Default Parsing Model + +By default, uploaded PDF files are parsed using `gpt-4o`. You can customize this by setting the following: + +```bash +# Override the default parsing model +export FILE_PARSING_MODEL=your-preferred-model +``` + +You can also change the model provider like so: +```bash +# Optional: Override the parsing provider settings +export FILE_PARSING_PROVIDER_BASE_URL=your-provider-base-url +export FILE_PARSING_PROVIDER_API_KEY=your-provider-api-key +``` + +### 3. Embedding Model Options + +By default, Rowboat uses OpenAI's `text-embedding-3-small` model for generating embeddings. You can customize this by setting the following: + +```bash +# Override the default embedding model +export EMBEDDING_MODEL=your-preferred-model +export EMBEDDING_VECTOR_SIZE=1536 +``` + +**Important NOTE** + +The default size for the vectors index is 1536. If you change this value, then you must delete the index and set it up again: +```bash +docker-compose --profile delete_qdrant --profile qdrant up --build delete_qdrant qdrant +``` +followed by: +```bash +./start # this will recreate the index +``` + +You can also change the model provider like so: +```bash +# Optional: Override the embedding provider settings +export EMBEDDING_PROVIDER_BASE_URL=your-provider-base-url +export EMBEDDING_PROVIDER_API_KEY=your-provider-api-key +``` + +If you don't specify the provider settings, Rowboat will use OpenAI as the default provider. diff --git a/apps/docs/docs/videos/Intro-Video.gif b/apps/docs/docs/videos/Intro-Video.gif new file mode 100644 index 000000000..d4eb387ef Binary files /dev/null and b/apps/docs/docs/videos/Intro-Video.gif differ diff --git a/apps/docs/docs/videos/intro.mp4 b/apps/docs/docs/videos/intro.mp4 new file mode 100644 index 000000000..4306a886e Binary files /dev/null and b/apps/docs/docs/videos/intro.mp4 differ diff --git a/apps/docs/public/videos/Intro-Video.gif b/apps/docs/public/videos/Intro-Video.gif new file mode 100644 index 000000000..d4eb387ef Binary files /dev/null and b/apps/docs/public/videos/Intro-Video.gif differ diff --git a/apps/rowboat/app/actions/copilot.actions.ts b/apps/rowboat/app/actions/copilot.actions.ts index 5712e0afe..de9b1baec 100644 --- a/apps/rowboat/app/actions/copilot.actions.ts +++ b/apps/rowboat/app/actions/copilot.actions.ts @@ -1,22 +1,25 @@ 'use server'; -import { +import { CopilotAPIRequest, CopilotChatContext, CopilotMessage, DataSourceSchemaForCopilot, -} from "../../src/application/lib/copilot/types"; +} from "../../src/entities/models/copilot"; import { Workflow} from "../lib/types/workflow_types"; import { z } from 'zod'; import { projectAuthCheck } from "./project.actions"; -import { redisClient } from "../lib/redis"; import { authorizeUserAction, logUsage } from "./billing.actions"; import { USE_BILLING } from "../lib/feature_flags"; import { getEditAgentInstructionsResponse } from "../../src/application/lib/copilot/copilot"; import { container } from "@/di/container"; import { IUsageQuotaPolicy } from "@/src/application/policies/usage-quota.policy.interface"; import { UsageTracker } from "../lib/billing"; +import { authCheck } from "./auth.actions"; +import { ICreateCopilotCachedTurnController } from "@/src/interface-adapters/controllers/copilot/create-copilot-cached-turn.controller"; +import { BillingError } from "@/src/entities/errors/common"; const usageQuotaPolicy = container.resolve('usageQuotaPolicy'); +const createCopilotCachedTurnController = container.resolve('createCopilotCachedTurnController'); export async function getCopilotResponseStream( projectId: string, @@ -27,40 +30,29 @@ export async function getCopilotResponseStream( ): Promise<{ streamId: string; } | { billingError: string }> { - await projectAuthCheck(projectId); - await usageQuotaPolicy.assertAndConsumeProjectAction(projectId); + const user = await authCheck(); - // Check billing authorization - const authResponse = await authorizeUserAction({ - type: 'use_credits', - }); - if (!authResponse.success) { - return { billingError: authResponse.error || 'Billing error' }; + try { + const { key } = await createCopilotCachedTurnController.execute({ + caller: 'user', + userId: user.id, + data: { + projectId, + messages, + workflow: current_workflow_config, + context, + dataSources, + } + }); + return { + streamId: key, + }; + } catch (err) { + if (err instanceof BillingError) { + return { billingError: err.message }; + } + throw err; } - - await usageQuotaPolicy.assertAndConsumeProjectAction(projectId); - - // prepare request - const request: z.infer = { - projectId, - messages, - workflow: current_workflow_config, - context, - dataSources: dataSources, - }; - - // serialize the request - const payload = JSON.stringify(request); - - // create a uuid for the stream - const streamId = crypto.randomUUID(); - - // store payload in redis - await redisClient.set(`copilot-stream-${streamId}`, payload, 'EX', 60 * 10); // expire in 10 minutes - - return { - streamId, - }; } export async function getCopilotAgentInstructions( diff --git a/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts b/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts index 1e5409336..620dfd00c 100644 --- a/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts +++ b/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts @@ -1,70 +1,45 @@ -import { getCustomerIdForProject, logUsage, UsageTracker } from "@/app/lib/billing"; -import { USE_BILLING } from "@/app/lib/feature_flags"; -import { redisClient } from "@/app/lib/redis"; -import { CopilotAPIRequest } from "@/src/application/lib/copilot/types"; -import { streamMultiAgentResponse } from "@/src/application/lib/copilot/copilot"; +import { container } from "@/di/container"; +import { IRunCopilotCachedTurnController } from "@/src/interface-adapters/controllers/copilot/run-copilot-cached-turn.controller"; +import { requireAuth } from "@/app/lib/auth"; export const maxDuration = 300; export async function GET(request: Request, props: { params: Promise<{ streamId: string }> }) { const params = await props.params; - // get the payload from redis - const payload = await redisClient.get(`copilot-stream-${params.streamId}`); - if (!payload) { - return new Response("Stream not found", { status: 404 }); - } - // parse the payload - const { projectId, context, messages, workflow, dataSources } = CopilotAPIRequest.parse(JSON.parse(payload)); + // get user data + const user = await requireAuth(); - // fetch billing customer id - let billingCustomerId: string | null = null; - if (USE_BILLING) { - billingCustomerId = await getCustomerIdForProject(projectId); - } + const runCopilotCachedTurnController = container.resolve("runCopilotCachedTurnController"); - const usageTracker = new UsageTracker(); const encoder = new TextEncoder(); - let messageCount = 0; const stream = new ReadableStream({ async start(controller) { try { // Iterate over the copilot stream generator - for await (const event of streamMultiAgentResponse( - usageTracker, - projectId, - context, - messages, - workflow, - dataSources || [], - )) { + for await (const event of runCopilotCachedTurnController.execute({ + caller: "user", + userId: user.id, + apiKey: request.headers.get("Authorization")?.split(" ")[1], + key: params.streamId, + })) { // Check if this is a content event if ('content' in event) { - messageCount++; controller.enqueue(encoder.encode(`event: message\ndata: ${JSON.stringify(event)}\n\n`)); } else if ('type' in event && event.type === 'tool-call') { controller.enqueue(encoder.encode(`event: tool-call\ndata: ${JSON.stringify(event)}\n\n`)); } else if ('type' in event && event.type === 'tool-result') { controller.enqueue(encoder.encode(`event: tool-result\ndata: ${JSON.stringify(event)}\n\n`)); - } else { - controller.enqueue(encoder.encode(`event: done\ndata: ${JSON.stringify(event)}\n\n`)); } } } catch (error) { console.error('Error processing copilot stream:', error); controller.error(new Error("Something went wrong. Please try again.")); } finally { - // log copilot usage - if (USE_BILLING && billingCustomerId) { - try { - await logUsage(billingCustomerId, { - items: usageTracker.flush(), - }); - } catch (error) { - console.error("Error logging usage", error); - } - } + console.log("closing stream"); + controller.enqueue(encoder.encode(`event: done\ndata: ${JSON.stringify({ type: 'done' })}\n\n`)); + controller.enqueue(encoder.encode("event: end\n\n")); controller.close(); } }, diff --git a/apps/rowboat/app/lib/feature_flags.ts b/apps/rowboat/app/lib/feature_flags.ts index a80df7c6f..f2ddd7bd2 100644 --- a/apps/rowboat/app/lib/feature_flags.ts +++ b/apps/rowboat/app/lib/feature_flags.ts @@ -17,4 +17,5 @@ export const USE_PRODUCT_TOUR = false; export const SHOW_COPILOT_MARQUEE = false; export const SHOW_PROMPTS_SECTION = true; export const SHOW_DARK_MODE_TOGGLE = false; -export const SHOW_VISUALIZATION = false \ No newline at end of file +export const SHOW_VISUALIZATION = false; +export const SHOW_PREBUILT_CARDS = false; \ No newline at end of file diff --git a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx index fc9675fe3..2e901e7ff 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx @@ -2,8 +2,8 @@ import { Button } from "@/components/ui/button"; import { Dropdown, DropdownItem, DropdownMenu, DropdownSection, DropdownTrigger, Spinner, Tooltip } from "@heroui/react"; import { useRef, useState, createContext, useContext, useCallback, forwardRef, useImperativeHandle, useEffect, Ref } from "react"; -import { CopilotChatContext } from "../../../../src/application/lib/copilot/types"; -import { CopilotMessage } from "../../../../src/application/lib/copilot/types"; +import { CopilotChatContext } from "../../../../src/entities/models/copilot"; +import { CopilotMessage } from "../../../../src/entities/models/copilot"; import { Workflow } from "@/app/lib/types/workflow_types"; import { DataSource } from "@/src/entities/models/data-source"; import { z } from "zod"; diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx index 001bb89cd..e67ca589a 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx @@ -2,7 +2,7 @@ import { createContext, useContext, useRef, useState } from "react"; import clsx from "clsx"; import { z } from "zod"; -import { CopilotAssistantMessageActionPart } from "../../../../../src/application/lib/copilot/types"; +import { CopilotAssistantMessageActionPart } from "../../../../../src/entities/models/copilot"; import { Workflow } from "../../../../lib/types/workflow_types"; import { PreviewModalProvider, usePreviewModal } from '../../workflow/preview-modal'; import { getAppliedChangeKey } from "../app"; diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx index 2b891084e..c0e9865c0 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx @@ -5,7 +5,7 @@ import { z } from "zod"; import { Workflow} from "@/app/lib/types/workflow_types"; import MarkdownContent from "@/app/lib/components/markdown-content"; import { MessageSquareIcon, EllipsisIcon, XIcon, CheckCheckIcon, ChevronDown, ChevronUp } from "lucide-react"; -import { CopilotMessage, CopilotAssistantMessage, CopilotAssistantMessageActionPart } from "@/src/application/lib/copilot/types"; +import { CopilotMessage, CopilotAssistantMessage, CopilotAssistantMessageActionPart } from "@/src/entities/models/copilot"; import { Action, StreamingAction } from './actions'; import { useParsedBlocks } from "../use-parsed-blocks"; import { validateConfigChanges } from "@/app/lib/client_utils"; diff --git a/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx b/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx index 70e6da1b0..fb29ce108 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx @@ -1,6 +1,6 @@ import { useCallback, useRef, useState } from "react"; import { getCopilotResponseStream } from "@/app/actions/copilot.actions"; -import { CopilotMessage } from "@/src/application/lib/copilot/types"; +import { CopilotMessage } from "@/src/entities/models/copilot"; import { Workflow } from "@/app/lib/types/workflow_types"; import { DataSource } from "@/src/entities/models/data-source"; import { z } from "zod"; diff --git a/apps/rowboat/app/projects/[projectId]/entities/agent_config.tsx b/apps/rowboat/app/projects/[projectId]/entities/agent_config.tsx index 15abca57d..3ad2c96ae 100644 --- a/apps/rowboat/app/projects/[projectId]/entities/agent_config.tsx +++ b/apps/rowboat/app/projects/[projectId]/entities/agent_config.tsx @@ -7,7 +7,7 @@ import { useState, useEffect, useRef } from "react"; import { usePreviewModal } from "../workflow/preview-modal"; import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Select, SelectItem, Chip, SelectSection, Input } from "@heroui/react"; import { PreviewModalProvider } from "../workflow/preview-modal"; -import { CopilotMessage } from "@/src/application/lib/copilot/types"; +import { CopilotMessage } from "@/src/entities/models/copilot"; import { getCopilotAgentInstructions } from "@/app/actions/copilot.actions"; import { Dropdown as CustomDropdown } from "../../../lib/components/dropdown"; import { createAtMentions } from "../../../lib/components/atmentions"; diff --git a/apps/rowboat/app/projects/[projectId]/tools/components/SelectComposioToolkit.tsx b/apps/rowboat/app/projects/[projectId]/tools/components/SelectComposioToolkit.tsx index fa4fc41e6..51beffa4e 100644 --- a/apps/rowboat/app/projects/[projectId]/tools/components/SelectComposioToolkit.tsx +++ b/apps/rowboat/app/projects/[projectId]/tools/components/SelectComposioToolkit.tsx @@ -132,10 +132,10 @@ export function SelectComposioToolkit({

{filterByTriggers - ? 'Loading Composio toolkits with triggers...' + ? 'Loading toolkits with triggers...' : filterByTools - ? 'Loading Composio toolkits with tools...' - : 'Loading Composio toolkits...' + ? 'Loading toolkits with tools...' + : 'Loading toolkits...' }

diff --git a/apps/rowboat/app/projects/components/build-assistant-section.tsx b/apps/rowboat/app/projects/components/build-assistant-section.tsx index 4528917c8..50809578b 100644 --- a/apps/rowboat/app/projects/components/build-assistant-section.tsx +++ b/apps/rowboat/app/projects/components/build-assistant-section.tsx @@ -16,11 +16,35 @@ import { Tabs, Tab } from "@/components/ui/tabs"; import { Project } from "@/src/entities/models/project"; import { z } from "zod"; import Link from 'next/link'; +import { SHOW_PREBUILT_CARDS } from '@/app/lib/feature_flags'; const ITEMS_PER_PAGE = 6; +const copilotPrompts = { + "Blog assistant": { + prompt: "Build an assistant to help with writing a blog post and updating it on google docs", + emoji: "πŸ“" + }, + "Meeting prep workflow": { + prompt: "Build a meeting prep pipeline which takes a google calendar invite as input and performs research on the guests using Duckduckgo search and send an email to me", + emoji: "πŸ“…" + }, + "Scheduling assistant": { + prompt: "Build a scheduling assistant that helps users manage their calendar, book meetings, find available time slots, send reminders, and optimize their daily schedule based on priorities and preferences", + emoji: "βœ…" + }, + "Reddit & HN assistant": { + prompt: "Build an assistant that helps me with browsing Reddit and Hacker News", + emoji: "πŸ”" + }, + "CRM assistant": { + prompt: "Build an assistant that helps me with my CRM", + emoji: "πŸ“Š" + } +}; + export function BuildAssistantSection() { const [userPrompt, setUserPrompt] = useState(''); const [isCreating, setIsCreating] = useState(false); @@ -85,6 +109,12 @@ export function BuildAssistantSection() { await createProjectFromTemplate(templateId, router); }; + // Handle prompt card selection + const handlePromptSelect = (promptText: string) => { + setUserPrompt(promptText); + setPromptError(null); + }; + const fetchProjects = async () => { setProjectsLoading(true); try { @@ -310,6 +340,24 @@ export function BuildAssistantSection() { + + {/* Predefined Prompt Cards */} +
+
+ {Object.entries(copilotPrompts).map(([name, config]) => ( + + ))} +
+
@@ -398,7 +446,7 @@ export function BuildAssistantSection() { {/* Pre-built Assistants Section - Only show for New Assistant tab */} - {selectedTab === 'new' && ( + {selectedTab === 'new' && SHOW_PREBUILT_CARDS && (
diff --git a/apps/rowboat/components/common/help-modal.tsx b/apps/rowboat/components/common/help-modal.tsx index c45eb6bea..c3a4edba4 100644 --- a/apps/rowboat/components/common/help-modal.tsx +++ b/apps/rowboat/components/common/help-modal.tsx @@ -56,7 +56,7 @@ export function HelpModal({ isOpen, onClose, onStartTour }: HelpModalProps) { docs or reach out on discord.', + content: 'Come back here anytime to restart the tour.\nStill have questions? See our docs or reach out on discord.', title: 'Step 9/9' } ]; diff --git a/apps/rowboat/di/container.ts b/apps/rowboat/di/container.ts index be9bf3441..456250c85 100644 --- a/apps/rowboat/di/container.ts +++ b/apps/rowboat/di/container.ts @@ -145,6 +145,12 @@ import { UpdateLiveWorkflowController } from "@/src/interface-adapters/controlle import { RevertToLiveWorkflowUseCase } from "@/src/application/use-cases/projects/revert-to-live-workflow.use-case"; import { RevertToLiveWorkflowController } from "@/src/interface-adapters/controllers/projects/revert-to-live-workflow.controller"; +// copilot +import { CreateCopilotCachedTurnUseCase } from "@/src/application/use-cases/copilot/create-copilot-cached-turn.use-case"; +import { CreateCopilotCachedTurnController } from "@/src/interface-adapters/controllers/copilot/create-copilot-cached-turn.controller"; +import { RunCopilotCachedTurnUseCase } from "@/src/application/use-cases/copilot/run-copilot-cached-turn.use-case"; +import { RunCopilotCachedTurnController } from "@/src/interface-adapters/controllers/copilot/run-copilot-cached-turn.controller"; + // users import { MongoDBUsersRepository } from "@/src/infrastructure/repositories/mongodb.users.repository"; @@ -328,6 +334,13 @@ container.register({ listConversationsController: asClass(ListConversationsController).singleton(), fetchConversationController: asClass(FetchConversationController).singleton(), + // copilot + // --- + createCopilotCachedTurnUseCase: asClass(CreateCopilotCachedTurnUseCase).singleton(), + createCopilotCachedTurnController: asClass(CreateCopilotCachedTurnController).singleton(), + runCopilotCachedTurnUseCase: asClass(RunCopilotCachedTurnUseCase).singleton(), + runCopilotCachedTurnController: asClass(RunCopilotCachedTurnController).singleton(), + // users // --- usersRepository: asClass(MongoDBUsersRepository).singleton(), diff --git a/apps/rowboat/src/application/lib/agents-runtime/agent_instructions.ts b/apps/rowboat/src/application/lib/agents-runtime/agent_instructions.ts index 7fc494432..56a752a67 100644 --- a/apps/rowboat/src/application/lib/agents-runtime/agent_instructions.ts +++ b/apps/rowboat/src/application/lib/agents-runtime/agent_instructions.ts @@ -140,7 +140,7 @@ export const PIPELINE_TYPE_INSTRUCTIONS = (): string => ` - Provide clear, actionable output that the next pipeline step can easily understand and work with. - Do NOT attempt to handle tasks outside your specific pipeline role. - Do NOT mention other agents or the pipeline structure to users. -- Your response should be self-contained and ready to be consumed by the next pipeline step. +- Your response should be self-contained and ready to be consumed by the next pipeline step. Add a prefix 'Internal message' to your response. - Reading the message history will show you the pipeline execution flow up to your step. - These are high level instructions only. The user will provide more specific instructions which will be below. `; diff --git a/apps/rowboat/src/application/lib/copilot/copilot.ts b/apps/rowboat/src/application/lib/copilot/copilot.ts index 4a60fe94b..d8b82614c 100644 --- a/apps/rowboat/src/application/lib/copilot/copilot.ts +++ b/apps/rowboat/src/application/lib/copilot/copilot.ts @@ -2,7 +2,7 @@ import z from "zod"; import { createOpenAI } from "@ai-sdk/openai"; import { generateObject, streamText, tool } from "ai"; import { Workflow, WorkflowTool } from "@/app/lib/types/workflow_types"; -import { CopilotChatContext, CopilotMessage, DataSourceSchemaForCopilot } from "./types"; +import { CopilotChatContext, CopilotMessage, DataSourceSchemaForCopilot } from "../../../entities/models/copilot"; import { PrefixLogger } from "@/app/lib/utils"; import zodToJsonSchema from "zod-to-json-schema"; import { COPILOT_INSTRUCTIONS_EDIT_AGENT } from "./copilot_edit_agent"; @@ -12,6 +12,7 @@ import { CURRENT_WORKFLOW_PROMPT } from "./current_workflow"; import { USE_COMPOSIO_TOOLS } from "@/app/lib/feature_flags"; import { composio, getTool } from "../composio/composio"; import { UsageTracker } from "@/app/lib/billing"; +import { CopilotStreamEvent } from "@/src/entities/models/copilot"; const PROVIDER_API_KEY = process.env.PROVIDER_API_KEY || process.env.OPENAI_API_KEY || ''; const PROVIDER_BASE_URL = process.env.PROVIDER_BASE_URL || undefined; @@ -35,30 +36,6 @@ const openai = createOpenAI({ compatibility: "strict", }); -const ZTextEvent = z.object({ - content: z.string(), -}); - -const ZToolCallEvent = z.object({ - type: z.literal('tool-call'), - toolName: z.string(), - toolCallId: z.string(), - args: z.record(z.any()), - query: z.string().optional(), -}); - -const ZToolResultEvent = z.object({ - type: z.literal('tool-result'), - toolCallId: z.string(), - result: z.any(), -}); - -const ZDoneEvent = z.object({ - done: z.literal(true), -}); - -const ZEvent = z.union([ZTextEvent, ZToolCallEvent, ZToolResultEvent, ZDoneEvent]); - const composioToolSearchToolSuggestion = z.object({ toolkit: z.string(), tool_slug: z.string(), @@ -273,7 +250,7 @@ export async function* streamMultiAgentResponse( messages: z.infer[], workflow: z.infer, dataSources: z.infer[] -): AsyncIterable> { +): AsyncIterable> { const logger = new PrefixLogger('copilot /stream'); logger.log('context', context); logger.log('projectId', projectId); @@ -375,9 +352,4 @@ export async function* streamMultiAgentResponse( projectId, totalChunks: chunkCount }); - - // done - yield { - done: true, - }; } \ No newline at end of file diff --git a/apps/rowboat/src/application/use-cases/copilot/create-copilot-cached-turn.use-case.ts b/apps/rowboat/src/application/use-cases/copilot/create-copilot-cached-turn.use-case.ts new file mode 100644 index 000000000..20f70a927 --- /dev/null +++ b/apps/rowboat/src/application/use-cases/copilot/create-copilot-cached-turn.use-case.ts @@ -0,0 +1,87 @@ +import { z } from "zod"; +import { nanoid } from 'nanoid'; +import { ICacheService } from '@/src/application/services/cache.service.interface'; +import { IUsageQuotaPolicy } from '@/src/application/policies/usage-quota.policy.interface'; +import { IProjectActionAuthorizationPolicy } from '@/src/application/policies/project-action-authorization.policy'; +import { CopilotChatContext, CopilotMessage, DataSourceSchemaForCopilot } from '@/src/entities/models/copilot'; +import { Workflow } from '@/app/lib/types/workflow_types'; +import { USE_BILLING } from "@/app/lib/feature_flags"; +import { authorize, getCustomerIdForProject } from "@/app/lib/billing"; +import { BillingError } from "@/src/entities/errors/common"; + +const inputSchema = z.object({ + caller: z.enum(["user", "api"]), + userId: z.string().optional(), + apiKey: z.string().optional(), + data: z.object({ + projectId: z.string(), + messages: z.array(CopilotMessage), + workflow: Workflow, + context: CopilotChatContext.nullable(), + dataSources: z.array(DataSourceSchemaForCopilot).optional(), + }), +}); + +export interface ICreateCopilotCachedTurnUseCase { + execute(data: z.infer): Promise<{ key: string }>; +} + +export class CreateCopilotCachedTurnUseCase implements ICreateCopilotCachedTurnUseCase { + private readonly cacheService: ICacheService; + private readonly usageQuotaPolicy: IUsageQuotaPolicy; + private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy; + + constructor({ + cacheService, + usageQuotaPolicy, + projectActionAuthorizationPolicy, + }: { + cacheService: ICacheService, + usageQuotaPolicy: IUsageQuotaPolicy, + projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, + }) { + this.cacheService = cacheService; + this.usageQuotaPolicy = usageQuotaPolicy; + this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy; + } + + async execute(data: z.infer): Promise<{ key: string }> { + const { projectId } = data.data; + + // check auth + await this.projectActionAuthorizationPolicy.authorize({ + projectId, + caller: data.caller, + userId: data.userId, + apiKey: data.apiKey, + }); + await this.usageQuotaPolicy.assertAndConsumeProjectAction(projectId); + + // check billing authorization + if (USE_BILLING) { + // get billing customer id for this project + const billingCustomerId = await getCustomerIdForProject(projectId); + + // validate enough credits + const result = await authorize(billingCustomerId, { + type: "use_credits" + }); + if (!result.success) { + throw new BillingError(result.error || 'Billing error'); + } + } + + // serialize request + const payload = JSON.stringify(data.data); + + // create unique id for stream + const key = nanoid(); + + // store in cache + await this.cacheService.set(`copilot-stream-${key}`, payload, 60 * 10); // expire in 10 minutes + + return { + key, + } + } +} \ No newline at end of file diff --git a/apps/rowboat/src/application/use-cases/copilot/run-copilot-cached-turn.use-case.ts b/apps/rowboat/src/application/use-cases/copilot/run-copilot-cached-turn.use-case.ts new file mode 100644 index 000000000..7afb0a29e --- /dev/null +++ b/apps/rowboat/src/application/use-cases/copilot/run-copilot-cached-turn.use-case.ts @@ -0,0 +1,104 @@ +import { z } from "zod"; +import { ICacheService } from '@/src/application/services/cache.service.interface'; +import { IUsageQuotaPolicy } from '@/src/application/policies/usage-quota.policy.interface'; +import { IProjectActionAuthorizationPolicy } from '@/src/application/policies/project-action-authorization.policy'; +import { CopilotAPIRequest, CopilotStreamEvent } from '@/src/entities/models/copilot'; +import { USE_BILLING } from "@/app/lib/feature_flags"; +import { authorize, getCustomerIdForProject, logUsage, UsageTracker } from "@/app/lib/billing"; +import { BillingError, NotFoundError } from "@/src/entities/errors/common"; +import { streamMultiAgentResponse } from "@/src/application/lib/copilot/copilot"; + +const inputSchema = z.object({ + caller: z.enum(["user", "api"]), + userId: z.string().optional(), + apiKey: z.string().optional(), + key: z.string(), +}); + +export interface IRunCopilotCachedTurnUseCase { + execute(data: z.infer): AsyncGenerator, void, unknown>; +} + +export class RunCopilotCachedTurnUseCase implements IRunCopilotCachedTurnUseCase { + private readonly cacheService: ICacheService; + private readonly usageQuotaPolicy: IUsageQuotaPolicy; + private readonly projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy; + + constructor({ + cacheService, + usageQuotaPolicy, + projectActionAuthorizationPolicy, + }: { + cacheService: ICacheService, + usageQuotaPolicy: IUsageQuotaPolicy, + projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy, + }) { + this.cacheService = cacheService; + this.usageQuotaPolicy = usageQuotaPolicy; + this.projectActionAuthorizationPolicy = projectActionAuthorizationPolicy; + } + + async *execute(data: z.infer): AsyncGenerator, void, unknown> { + // fetch cached turn + const lookupKey = `copilot-stream-${data.key}`; + const payload = await this.cacheService.get(lookupKey); + if (!payload) { + throw new NotFoundError('Cached turn not found'); + } + + // delete from cache + await this.cacheService.delete(lookupKey); + + // parse cached turn + const cachedTurn = CopilotAPIRequest.parse(JSON.parse(payload)); + + const { projectId } = cachedTurn; + + // check auth + await this.projectActionAuthorizationPolicy.authorize({ + projectId, + caller: data.caller, + userId: data.userId, + apiKey: data.apiKey, + }); + + await this.usageQuotaPolicy.assertAndConsumeProjectAction(projectId); + + // check billing authorization + let billingCustomerId: string | null = null; + if (USE_BILLING) { + // get billing customer id for this project + billingCustomerId = await getCustomerIdForProject(projectId); + + // validate enough credits + const result = await authorize(billingCustomerId, { + type: "use_credits" + }); + if (!result.success) { + throw new BillingError(result.error || 'Billing error'); + } + } + + // init usage tracking + const usageTracker = new UsageTracker(); + + try { + for await (const event of streamMultiAgentResponse( + usageTracker, + projectId, + cachedTurn.context, + cachedTurn.messages, + cachedTurn.workflow, + cachedTurn.dataSources || [], + )) { + yield event; + } + } finally { + if (USE_BILLING && billingCustomerId) { + await logUsage(billingCustomerId, { + items: usageTracker.flush(), + }); + } + } + } +} \ No newline at end of file diff --git a/apps/rowboat/src/application/lib/copilot/types.ts b/apps/rowboat/src/entities/models/copilot.ts similarity index 78% rename from apps/rowboat/src/application/lib/copilot/types.ts rename to apps/rowboat/src/entities/models/copilot.ts index 5b54f9dc3..eeff97423 100644 --- a/apps/rowboat/src/application/lib/copilot/types.ts +++ b/apps/rowboat/src/entities/models/copilot.ts @@ -68,4 +68,28 @@ export const CopilotAPIResponse = z.union([ z.object({ error: z.string(), }), +]); + +const CopilotStreamTextEvent = z.object({ + content: z.string(), +}); + +const CopilotStreamToolCallEvent = z.object({ + type: z.literal('tool-call'), + toolName: z.string(), + toolCallId: z.string(), + args: z.record(z.any()), + query: z.string().optional(), +}); + +const CopilotStreamToolResultEvent = z.object({ + type: z.literal('tool-result'), + toolCallId: z.string(), + result: z.any(), +}); + +export const CopilotStreamEvent = z.union([ + CopilotStreamTextEvent, + CopilotStreamToolCallEvent, + CopilotStreamToolResultEvent, ]); \ No newline at end of file diff --git a/apps/rowboat/src/interface-adapters/controllers/copilot/create-copilot-cached-turn.controller.ts b/apps/rowboat/src/interface-adapters/controllers/copilot/create-copilot-cached-turn.controller.ts new file mode 100644 index 000000000..bd14730e0 --- /dev/null +++ b/apps/rowboat/src/interface-adapters/controllers/copilot/create-copilot-cached-turn.controller.ts @@ -0,0 +1,44 @@ +import { z } from "zod"; +import { CopilotChatContext, CopilotMessage, DataSourceSchemaForCopilot } from '@/src/entities/models/copilot'; +import { Workflow } from '@/app/lib/types/workflow_types'; +import { ICreateCopilotCachedTurnUseCase } from "@/src/application/use-cases/copilot/create-copilot-cached-turn.use-case"; +import { BadRequestError } from "@/src/entities/errors/common"; + +const inputSchema = z.object({ + caller: z.enum(["user", "api"]), + userId: z.string().optional(), + apiKey: z.string().optional(), + data: z.object({ + projectId: z.string(), + messages: z.array(CopilotMessage), + workflow: Workflow, + context: CopilotChatContext.nullable(), + dataSources: z.array(DataSourceSchemaForCopilot).optional(), + }), +}); + +export interface ICreateCopilotCachedTurnController { + execute(request: z.infer): Promise<{ key: string }>; +} + +export class CreateCopilotCachedTurnController implements ICreateCopilotCachedTurnController { + private readonly createCopilotCachedTurnUseCase: ICreateCopilotCachedTurnUseCase; + + constructor({ + createCopilotCachedTurnUseCase, + }: { + createCopilotCachedTurnUseCase: ICreateCopilotCachedTurnUseCase, + }) { + this.createCopilotCachedTurnUseCase = createCopilotCachedTurnUseCase; + } + + async execute(request: z.infer): Promise<{ key: string }> { + // parse input + const result = inputSchema.safeParse(request); + if (!result.success) { + throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`); + } + + return await this.createCopilotCachedTurnUseCase.execute(result.data); + } +} \ No newline at end of file diff --git a/apps/rowboat/src/interface-adapters/controllers/copilot/run-copilot-cached-turn.controller.ts b/apps/rowboat/src/interface-adapters/controllers/copilot/run-copilot-cached-turn.controller.ts new file mode 100644 index 000000000..d62ad3fd7 --- /dev/null +++ b/apps/rowboat/src/interface-adapters/controllers/copilot/run-copilot-cached-turn.controller.ts @@ -0,0 +1,37 @@ +import { z } from "zod"; +import { CopilotStreamEvent } from '@/src/entities/models/copilot'; +import { IRunCopilotCachedTurnUseCase } from "@/src/application/use-cases/copilot/run-copilot-cached-turn.use-case"; +import { BadRequestError } from "@/src/entities/errors/common"; + +const inputSchema = z.object({ + caller: z.enum(["user", "api"]), + userId: z.string().optional(), + apiKey: z.string().optional(), + key: z.string(), +}); + +export interface IRunCopilotCachedTurnController { + execute(request: z.infer): AsyncGenerator, void, unknown>; +} + +export class RunCopilotCachedTurnController implements IRunCopilotCachedTurnController { + private readonly runCopilotCachedTurnUseCase: IRunCopilotCachedTurnUseCase; + + constructor({ + runCopilotCachedTurnUseCase, + }: { + runCopilotCachedTurnUseCase: IRunCopilotCachedTurnUseCase, + }) { + this.runCopilotCachedTurnUseCase = runCopilotCachedTurnUseCase; + } + + async *execute(request: z.infer): AsyncGenerator, void, unknown> { + // parse input + const result = inputSchema.safeParse(request); + if (!result.success) { + throw new BadRequestError(`Invalid request: ${JSON.stringify(result.error)}`); + } + + yield *this.runCopilotCachedTurnUseCase.execute(result.data); + } +} \ No newline at end of file