Skip to content

Commit

Permalink
Merge branch 'ft-add-entity-id-as-common-toolset' of github.com:Compo…
Browse files Browse the repository at this point in the history
…sioHQ/composio into ft-add-entity-id-as-common-toolset
  • Loading branch information
himanshu-dixit committed Jan 2, 2025
2 parents 8644a7b + 76ae2f5 commit 0607da9
Show file tree
Hide file tree
Showing 89 changed files with 7,015 additions and 37 deletions.
2 changes: 1 addition & 1 deletion docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
},
{
"name": "Chat with Code",
"url": "https://chatgpt.com/g/g-67697db23c808191a1787ea4b86ac1ce-composio"
"url": "https://entelligence.ai/ComposioHQ&composio"
}
],
"navigation": [
Expand Down
4 changes: 2 additions & 2 deletions docs/mint.json.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@
"url": "https://app.composio.dev/apps"
},
{
"name": "Chat with Repo",
"url": "https://dub.composio.dev/composio-chat-with-repo"
"name": "Chat with Code",
"url": "https://entelligence.ai/ComposioHQ&composio"
}
],
"navigation": [
Expand Down
41 changes: 41 additions & 0 deletions js/examples/chat-with-sheets/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
40 changes: 40 additions & 0 deletions js/examples/chat-with-sheets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/pages/api-reference/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.

[API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) instead of React pages.

This project uses [`next/font`](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn-pages-router) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/pages/building-your-application/deploying) for more details.
102 changes: 102 additions & 0 deletions js/examples/chat-with-sheets/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { VercelAIToolSet } from 'composio-core';
import { NextResponse } from "next/server";

export async function POST(req: Request) {
try {
const { messages } = await req.json();
let finalResult;

// Setup toolset
const toolset = new VercelAIToolSet({
apiKey: process.env.COMPOSIO_API_KEY,
});

async function setupUserConnectionIfNotExists(entityId: string | undefined) {
const entity = await toolset.client.getEntity(entityId);
const connection = await entity.getConnection({app: "googlesheets"});

if (!connection) {
// If this entity/user hasn't already connected the account
const connection = await entity.initiateConnection({appName: "googlesheets"});
console.log("Log in via: ", connection.redirectUrl);
return connection.waitUntilActive(60);
}

return connection;
}

async function executeAgent(entityName: string | undefined) {
// setup entity
const entity = await toolset.client.getEntity(entityName);
await setupUserConnectionIfNotExists(entity.id);
const spreadsheet_id = '1P9vE1IAZbI950cye58I6E4A3Uu8G-rcA4JAm52Pxnsw';
// get tools based on actions
const tools = await toolset.getTools({
actions: ["GOOGLESHEETS_BATCH_GET",
"GOOGLESHEETS_BATCH_UPDATE",
"GOOGLESHEETS_GET_SPREADSHEET_INFO",
"GOOGLESHEETS_CLEAR_VALUES",
"GOOGLESHEETS_CREATE_GOOGLE_SHEET1",
"TAVILY_TAVILY_SEARCH",
"CODEINTERPRETER_UPLOAD_FILE_CMD",
"CODEINTERPRETER_GET_FILE_CMD",
"CODEINTERPRETER_EXECUTE_CODE",
"CODEINTERPRETER_RUN_TERMINAL_CMD"
],
});

// Store both the AI response and tool execution result
const aiResponse = await generateText({
model: openai("gpt-4o"),
tools,
toolChoice: "auto",
system:`You are a sheets assistant. You have access to the user's google sheets and you can perform actions on it using the tools you have. Introduce yourself as a sheets agent. This is the id you need to perform actions on: ${spreadsheet_id}`,
messages: messages,
});

let finalResult = null;
if (aiResponse.toolCalls && aiResponse.toolCalls.length > 0) {
finalResult = await toolset.executeToolCall(
{
name: aiResponse.toolCalls[0].toolName,
arguments: aiResponse.toolCalls[0].args
},
entity.id
);
console.log(finalResult);
}
console.log(aiResponse);

const { text } = await generateText({
model: openai('gpt-4o'),
prompt: finalResult
? `Given the following user request: "${messages[messages.length - 1].content}", here's what happened: ${aiResponse.text} and the result was: ${finalResult}. Reveal the result of the tool call without markdown. This is the spreadsheet id that you need to use the actions on: ${spreadsheet_id}`
: `Print this same text, without adding any other text or sentences before or after: ${aiResponse.text}`,
});


return {
aitext: text,
aiResponse: aiResponse.text,
toolResult: finalResult
};
}

const result = await executeAgent("default");

// Return a structured response with both results
return NextResponse.json({
role: 'assistant',
content: `${result.aitext}`
});

} catch (error) {
console.error('Error:', error);
return NextResponse.json({
role: 'assistant',
content: 'Sorry, there was an error processing your request.'
}, { status: 500 });
}
}
16 changes: 16 additions & 0 deletions js/examples/chat-with-sheets/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
138 changes: 138 additions & 0 deletions js/examples/chat-with-sheets/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
'use client'
import React, { useState } from 'react';
import { Send, Bot, User } from 'lucide-react';
import "../styles/globals.css";

export default function ChatPage() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);

const handleSubmit = async (e) => {
e.preventDefault();
if (!input.trim()) return;

const userMessage = { role: 'user', content: input };
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);

try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [...messages, userMessage],
}),
});

if (!response.ok) throw new Error('Failed to fetch');

const data = await response.json();
setMessages(prev => [...prev, data]);
} catch (error) {
console.error('Error:', error);
setMessages(prev => [...prev, {
role: 'assistant',
content: 'Sorry, there was an error processing your request.'
}]);
} finally {
setIsLoading(false);
}
};

const formatContent = (content) => {
// Split content into AI response and execution result if it contains both
const parts = content.split('\n\nExecution Result: ');
if (parts.length === 2) {
try {
const executionResult = JSON.parse(parts[1]);
return (
<div className="space-y-2">
<div>{parts[0]}</div>
<div className="mt-2">
<div className="font-semibold text-blue-700">Execution Result:</div>
<pre className="bg-gray-100 p-2 rounded-md mt-1 text-sm overflow-x-auto">
{JSON.stringify(executionResult, null, 2)}
</pre>
</div>
</div>
);
} catch (e) {
return content;
}
}
return content;
};

return (
<div className="max-w-2xl mx-auto p-4 h-screen flex flex-col">
<div className="bg-white rounded-lg shadow-lg flex flex-col h-full">
<div className="p-4 border-b">
<h1 className="text-xl font-semibold text-center">Composio Google Sheets Agent</h1>
</div>

<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((message, index) => (
<div
key={index}
className={`flex items-start space-x-2 ${
message.role === 'assistant' ? 'justify-start' : 'justify-end'
}`}
>
{message.role === 'assistant' && (
<div className="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center">
<Bot className="w-5 h-5 text-blue-600" />
</div>
)}

<div
className={`max-w-[80%] rounded-lg p-3 ${
message.role === 'assistant'
? 'bg-blue-100 text-blue-900'
: 'bg-green-100 text-green-900'
}`}
>
{message.role === 'assistant'
? formatContent(message.content)
: message.content
}
</div>

{message.role === 'user' && (
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
<User className="w-5 h-5 text-green-600" />
</div>
)}
</div>
))}
</div>

<div className="border-t p-4">
<form onSubmit={handleSubmit} className="flex space-x-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
className="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
type="submit"
disabled={isLoading}
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
{isLoading ? (
<div className="w-6 h-6 border-2 border-white border-t-transparent rounded-full animate-spin" />
) : (
<Send className="w-5 h-5" />
)}
</button>
</form>
</div>
</div>
</div>
);
}
21 changes: 21 additions & 0 deletions js/examples/chat-with-sheets/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "styles/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
Loading

0 comments on commit 0607da9

Please sign in to comment.