getting started

This commit is contained in:
Atai Barkai
2024-10-17 01:29:37 -07:00
parent 058411579e
commit 25b5181088
3 changed files with 107 additions and 414 deletions
@@ -0,0 +1,44 @@
import React from "react";
export const CoAgentsDiagram: React.FC = (): JSX.Element => {
return (
<div className="flex flex-col items-center">
<div className="flex items-center">
<DiagramNode title="Frontend (via CopilotKit provider)" />
<DiagramArrow />
<DiagramNode title="Copilot Runtime" />
<DiagramArrow />
<DiagramNode title="Remote Endpoint (FastAPI + CopilotKit Python SDK)" />
<DiagramArrow />
<DiagramNode title="LangGraph Agent" />
</div>
</div>
);
};
interface DiagramNodeProps {
title: string;
}
const DiagramNode: React.FC<DiagramNodeProps> = ({ title }): JSX.Element => {
return (
<div className="bg-white dark:bg-neutral-800 shadow-lg rounded-lg p-4 m-2 text-center">
<span className="text-gray-800 dark:text-gray-200 font-medium">{title}</span>
</div>
);
};
const DiagramArrow: React.FC = (): JSX.Element => {
return (
<div className="mx-2">
<svg
className="w-6 h-6 text-gray-400 dark:text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
);
};
+58 -229
View File
@@ -5,271 +5,102 @@ description: "Get started with CoAgents in just a few minutes."
import { CoAgentsEnterpriseCTA } from "@/components/react/coagents/coagents-enterprise-cta.tsx";
import SelfHostingCopilotRuntimeCreateEndpoint from "@/snippets/self-hosting-copilot-runtime-create-endpoint.mdx";
import LLMAdapters from "@/snippets/llm-adapters.mdx";
import { CoAgentsDiagram } from "@/components/react/coagents/coagents-diagram.tsx";
## Before you start
These docs assume you’ve been introduced to CopilotKit’s core concepts and components. If you’re new to CopilotKit, take a minute to go through the quickstart and [learn how to integrate CopilotKit into a React app](/quickstart).
At the moment CoAgents integrates with LangGraph-powered agents using **LangGraph-Python**. <br/>
(Support for LangGraph.js and LangGraph Cloud is coming soon!)
Also, at the moment CoAgents integrates with LangGraph-powered agents using their Python SDK. (Support for LangGraph JS and LangGraph Cloud are coming soon!)
**For orientation, here's how CoAgents are wired:**
<CoAgentsDiagram />
<Callout type="info">
This guide assumes you’re familiar with using LangGraph to build agent workflows. If you need a quick introduction, check out [this brief example from the LangGraph docs](https://docs.langgraph.ai/quickstart).
</Callout>
## Getting started
<Steps>
<Step>
First, take a minute to **[go through the CopilotKit quickstart](/quickstart), and integrate CopilotKit into your React app.**
This should only take a minute.
</Step>
<Step>
Then, setup a CopilotKit Remote-Endpoint using FastAPI. This endpoint will serve your LangGraph agent.
Follow the [Remote Endpoint quickstart](/guides/backend-actions/remote-backend-endpoint).
</Step>
<Step>
## Setup a remote endpoint in Python + FastAPI
CoAgents require you to use a remote endpoint using Python, FastAPI, and the CopilotKit Python SDK; here's a basic example to get you started.
## Connect your FastAPI Remote Endpoint to a LangGraph agent
<Callout>
If you're starting from scratch or need more information, check out our [remote endpoint docs](/guides/backend-actions/remote-backend-endpoint) for a complete walkthrough.
</Callout>
In your Python project, install the CopilotKit SDK:
At this point you should have CopilotKit hooked into your application, connected to a FastAPI Remote Endpoint.
```bash
pip install copilotkit --extra-index-url https://copilotkit.gateway.scarf.sh/simple/
```
The next step is to configure the Remote Endpoint to serve LangGraph agents.
Then set up or update your FastAPI server to initialize our SDK and connect it to your LangGraph agent. In this example, we’re assuming a basic agent workflow is being exported from `agent.py` in the same directory as this server script.
**Modify your server.py file (setup in step (2)) to serve LangGraph agents:**
```python filename="server.py" showLineNumbers
```python title="server.py"
from fastapi import FastAPI
app = FastAPI()
# Import CopilotKit SDK and integrations
from copilotkit.integrations.fastapi import add_fastapi_endpoint
from copilotkit import CopilotKitSDK, LangGraphAgent
app = FastAPI()
# Import your LangGraph agent; in this example, it's the variable
# named `basic_agent_graph` in ./agent.py
from .agent import basic_agent_graph
# Initialize the agent for use by CopilotKit; we'll name it "basic_agent"
basic_agent = LangGraphAgent(
basic_agent = LangGraphAgent( // [!code highlight:4]
name="basic_agent",
description="Agent that asks about the weather",
agent=basic_agent_graph,
)
# Next, initialize the SDK, passing in the agent
sdk = CopilotKitSDK(
agents=[
basic_agent
],
)
# Finally, add the remote actions endpoint to this API
add_fastapi_endpoint(app, sdk, "/copilotkit_remote")
```
# Initialize the CopilotKit SDK
sdk = CopilotKitSDK(agents=[basic_agent]) [!code highlight:1]
# Add the CopilotKit endpoint to your FastAPI app
add_fastapi_endpoint(app, sdk, "/copilotkit_remote")
def main():
"""Run the uvicorn server."""
import uvicorn
uvicorn.run("server:app", host="127.0.0.1", port=8000, reload=True)
```
Remember the name `basic_agent` as well as our API endpoint URL (`http://localhost:8000/copilotkit_remote`), we'll need them as we move on to integrating this agent into the frontend.
</Step>
<Step>
## Self-host the Copilot Runtime
To use your remote agent service, you’ll also need to self-host the CopilotRuntime service. (Support for CopilotCloud is coming soon.)
### Configure an AI service adapter
First, we’ll create a new module file to initialize a runtime adapter for our LLM of choice, which we’ll put at `src/lib/serviceAdapter.ts`.
<Tabs items={['OpenAI', 'Anthropic', 'Other LLM Providers']}>
<Tab value="OpenAI">
Create a `.env` file in the root of your project, if you haven't already, and add your OpenAI API key.
```plaintext filename=".env"
OPENAI_API_KEY=your_api_key_here
```
<Callout type="warning" title="Do you have a paid API key?">
Please note that the code below uses GPT-4o, which requires a paid OpenAI API key.
**If you are using a free OpenAI API key**, change the model to a different option such as "gpt-3.5-turbo".
</Callout>
Then create a new file called `src/lib/llmAdapter.ts`:
```ts filename="src/lib/llmAdapter.ts"
import OpenAI from 'openai';
import { OpenAIAdapter } from '@copilotkit/runtime';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const openaiAdapter = new OpenAIAdapter({
openai,
model: "gpt-4o"
});
export default openaiAdapter
```
</Tab>
<Tab value="Anthropic">
Create a `.env` file in the root of your project, if you haven't already, and add your Anthropic API key.
```plaintext filename=".env"
ANTHROPIC_API_KEY=your_api_key_here
```
Then create a new file called `src/lib/llmAdapter.ts`:
```ts filename="src/lib/llmAdapter.ts"
import Anthropic from '@anthropic-ai/sdk';
import { AnthropicAdapter } from '@copilotkit/runtime';
const anthropic = new Anthropic({
apiKey: 'my_api_key', // defaults to process.env["ANTHROPIC_API_KEY"]
});
const anthropicAdapter = new AnthropicAdapter({ anthropic });
export default anthropicAdapter
```
</Tab>
<Tab value="Other LLM Providers">
To use a different LLM provider, you can follow the `OpenAI` instructions, but adapt them to use one of the other available LLM adapters listed below:
<LLMAdapters />
</Tab>
</Tabs>
### Add a CopilotRuntime endpoint to your app
Next, you'll use the service adapter you created above to initialize a `CopilotRuntime` instance and add a new route to your app. Here are some examples for the most common Node server frameworks:
<Tabs items={['Next.js App Router', 'Next.js Pages Router', 'Node.js Express']}>
{/* Next.js App Router */}
<Tab value="Next.js App Router">
Next, create a new server route which will make the `CopilotRuntime` available at `/api/copilotkit` and ready to work with remote actions provided by our Python app.
Create a new file at `src/app/api/copilotkit/route.ts` and add this code:
```ts filename="app/api/copilotkit/route.ts" showLineNumbers
import {
CopilotRuntime,
copilotRuntimeNextJSAppRouterEndpoint,
} from '@copilotkit/runtime';
import { NextRequest } from 'next/server';
import serviceAdapter from '@/lib/llmAdapter';
const runtime = new CopilotRuntime({
remoteActions: [
{
url: `http://localhost:8000/copilotkit_remote`,
}
]
});
export const POST = async (req: NextRequest) => {
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
runtime,
serviceAdapter,
endpoint: '/api/copilotkit',
});
return handleRequest(req);
};
```
</Tab>
{/* Next.js Pages Router */}
<Tab value="Next.js Pages Router">
Next, create a new server route which will make the `CopilotRuntime` available at `/api/copilotkit` and ready to work with remote actions provided by our Python app.
Create a new file at `src/pages/api/copilotkit.ts` and add this code:
```ts filename="pages/api/copilotkit.ts" showLineNumbers
import { NextApiRequest, NextApiResponse } from 'next';
import {
CopilotRuntime,
copilotRuntimeNextJSPagesRouterEndpoint,
} from '@copilotkit/runtime';
import serviceAdapter from '@/lib/llmAdapter';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const runtime = new CopilotRuntime({
remoteActions: [
{
url: `http://localhost:8000/copilotkit_remote`,
}
]
});
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
runtime,
serviceAdapter,
endpoint: '/api/copilotkit',
});
return await handleRequest(req, res);
};
export default handler;
```
</Tab>
{/* Node.js Express */}
<Tab value="Node.js Express">
Next, create a new route handler which will make the `CopilotRuntime` available at `/copilotkit` and ready to work with remote actions provided by our Python app:
```ts filename="server.ts" showLineNumbers
import express from 'express';
import {
CopilotRuntime,
copilotRuntimeNodeHttpEndpoint,
} from '@copilotkit/runtime';
import llmAdapter from '@/lib/llmAdapter';
const app = express();
app.use('/copilotkit', (req, res, next) => {
const runtime = new CopilotRuntime({
remoteActions: [
{
url: `http://localhost:8000/copilotkit_remote`,
}
]
});
const handler = copilotRuntimeNodeHttpEndpoint({
endpoint: '/copilotkit',
runtime,
serviceAdapter: llmAdapter,
});
return handler(req, res, next);
});
app.listen(4000, () => {
console.log('Listening at http://localhost:4000/copilotkit');
});
```
</Tab>
</Tabs>
</Step>
<Step>
## Integrate agents into your CopilotKit frontend app
## Agent-lock your Copilot to the `base_agent` agent.
Once you've set up your agents, registered them with CopilotKit on the Python side, and mounted the CopilotRuntime within your app, all that's left is to hook this up to your frontend using the `<CopilotKit />` component.
CopilotKit supports router-mode as well as agent-lock mode. For more detail see [router-mode / agent-mode](/coagents/advanced/router-mode-agent-lock).
For simplicity, wel'll use agent-lock mode in these tutorials, but in production use-cases you will likely want to use router-mode.
Lock the Copilot to the `basic_agent` setup earlier. This means every single interaction with the Copilot will be forwarded to the locked agent.
```tsx filename="src/page.tsx"
// This UI will now invoke our AI agent, not just the LLM
@@ -283,13 +114,11 @@ Once you've set up your agents, registered them with CopilotKit on the Python si
</CopilotKit>
```
By passing in the `agent` prop, we're telling CopilotKit to go into "agent lock" mode, and only use the `basic_agent` we set up earlier. To customize this behavior, see our docs about [router mode](/coagents/advanced/router-mode-agent-lock).
Any of CopilotKit's pre-built components can be used to interact with CoAgents, and you can also use agent-specific hooks in your custom UI components to enable powerful new workflows.
</Step>
</Steps>
🎉 Congrats! You've successfully integrated a LangGraph agent chatbot to your application! Give it a try now and see it in action.
<CoAgentsEnterpriseCTA />
+5 -185
View File
@@ -37,193 +37,13 @@ CopilotKit's CoAgents provides everything you need to deeply integrate LangGraph
<YouTubeVideo videoId="nBephBv4zr0" defaultPlaybackRate={1.25} />
</div>
## The Core Features of a CoAgent
<DynamicContentWrapper>
<CoAgentsFeatureToggle />
<CoAgentsFeatureRender feature="generative-ui">
### Generative UI
Generative UI allows you to create dynamic user interfaces with AI-generated components, inside a chat UI.
```tsx
useCoagentStateRender({
// the name of the agent
name: "car_rental_agent",
// this is optional: the node name to render generative UI for. If left empty, renders for every node.
nodeName: "confirm_node",
// render can be a function or a string, just like useCopilotAction
render: ({ state, status }) => {
if (status === "inProgress") {
return <div> Generating response: {state.response} </div>;
} else {
return <div> Done generating response </div>;
}
},
});
```
<Frame>
<img src="/images/coagents/json-feedback.png" alt="JSON feedback example" />
<img
src="/images/CoAgents.gif"
alt="CoAgents demonstration"
className="w-auto"
/>
</Frame>
</CoAgentsFeatureRender>
<CoAgentsFeatureRender feature="stream-agent-state">
### Stream agent state
<Steps>
### Frontend
All you need to do to get the current LangGraph agent state is call `useCoAgent`.
```tsx
const { state } = useCoAgent({
name: "search_agent", // the name of the agent
});
```
### LangGraph
The LangGraph state only updates when leaving a LangGraph node.
If you want to stream state updates to the frontend as they are generated, you can do so by one of the following methods:
#### Intercept tool calls
If you would like to stream **intermediate** state to the frontend, you can do so by treating (streaming) tool call arguments as intermediate state:
```python
# in python code:
config = copilotkit_customize_config(
config,
emit_intermediate_state=[
{
"state_key": "steps", # key on the LangGraph state to be updated
"tool": "search", # tool call
"tool_argument": "steps" # argument of the tool call interpreted as intermediate state
},
]
)
# ...
response = await ChatOpenAI(model="gpt-4").bind_tools([search_tool], parallel_tool_calls=False, tool_choice="search").ainvoke([
*state["messages"],
SystemMessage(
content=system_message
)
], config)
```
#### Manually Emit State
You can imperatively emit state by calling `copilotkit_emit_state`:
```python
# imperative function:
copilotkit_emit_state(config, {"key": value} )
```
#### Manually Emit Messages
To manually emit messages to the frontend, call `copilotkit_emit_message`:
```python
# imperative function:
copilotkit_emit_message(config, "Hello, world!")
```
</Steps>
<Frame>
<img src="/images/coagents/intermediate-state.gif" />
</Frame>
</CoAgentsFeatureRender>
<CoAgentsFeatureRender feature="share-agent-state">
### Share Agent State
Sharing agent state allows for collaboration and persistence across different sessions or users. Here's how you can implement this feature:
```typescript
const { state, setState, start, stop, run } = useCoAgent({
name: "search_agent",
});
// ...
setState(newStateEditedByUser);
run(() => new TextMessage({ role: MessageRole.System, content: "the user has updated the state" })); // optionally provide a hint about why the agent state changed.
```
Just like before, we use the `state` variable on `useCoAgent` to receive the latest agent state.
Now we additionally intercept 4 more of `useCoAgent`'s returned values:
- `setState`: A function to update the agent's state manually
- `start`: A function to initiate the agent's execution
- `stop`: A function to halt the agent's execution
- `run`: A function to re-run the agent, after updating the state. Optionally include a hint for why the state changed.
</CoAgentsFeatureRender>
<CoAgentsFeatureRender feature="agent-q-and-a">
### Agent Q&A
Agents can easily and intentionally ask the user questions. With support for:
- **Text Feedback:** Via the chat UI.
- **Structured (JSON) Feedback:** Via fully custom UI/UX anywhere in your application (chat UI, modals, popups, forms, etc).
<br />
<div className="flex gap-2 w-full my-2">
<div className="flex-1 border bg-white rounded-lg shadow-md p-4">
<div className="flex items-center justify-center gap-2 font-semibold">
<div className="bg-indigo-500 rounded-md w-7 h-7 text-white flex items-center justify-center">
<TextIcon className="w-4 h-4" />
</div>
<span className="dark:text-black">Text Feedback</span>
</div>
<div className="italic py-4">
<img
src="/images/coagents/text-feedback.png"
className="rounded-md border"
/>
</div>
</div>
<div className="flex-1 border bg-white rounded-lg shadow-md p-4">
<div className="flex items-center justify-center gap-2 font-semibold">
<div className="bg-indigo-500 rounded-md w-7 h-7 text-white flex items-center justify-center">
<JsonIcon className="w-5 h-5" />
</div>
<span className="dark:text-black">Structured (JSON) Feedback</span>
</div>
<div className="italic py-4">
<img
src="/images/coagents/json-feedback.png"
className="rounded-md border"
/>
</div>
</div>
</div>
We will soon add native & seamless Agent Q&A support in `useCoagentStateRender`.
In the meantime, you can achieve agent Q&A in the following way:
1. Use `useCoagentStateRender` to render your "Question" LangGraph node (via the exposed state).
2. In your LangGraph, go to an `END` node, or use a breakpoint, to wait for user input.
3. In the `useCoagentStateRender` render function, call `setState` provided by `useCoAgent` to provide the "answer" in the graph state.
4. After the answer is provided via the agent state, navigate to your relevant 'answer' node. For convenience, [you can use the `then` argument on LangGraph's `add_conditional_edges`](https://langchain-ai.github.io/langgraph/reference/graphs/#stategraph)
</CoAgentsFeatureRender>
<CoAgentsFeatureToggle />
</DynamicContentWrapper>
<CoAgentsEnterpriseCTA />