-
-
Save codesoda/4f74d5abc6f7daf64cbdfa950fa1d516 to your computer and use it in GitHub Desktop.
Generate a project proposal automatically from a meeting transcript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from fastapi import Request, HTTPException | |
from pydantic import BaseModel, BaseModel, HttpUrl | |
from modal import Secret, App, web_endpoint, Image | |
from typing import Optional, List | |
from example import proposal | |
import os | |
app = App(name="circleback", image=Image.debian_slim().pip_install("openai", "pydantic", "fastapi")) | |
class Attendee(BaseModel): | |
name: Optional[str] | |
email: Optional[str] | |
def markdown(self) -> str: | |
if self.name: | |
return f"- {self.name} ({self.email})" | |
return f"- {self.email}" | |
class Assignee(BaseModel): | |
name: Optional[str] | |
email: Optional[str] | |
def markdown(self) -> str: | |
if self.name: | |
return f"{self.name} ({self.email})" | |
return f"{self.email}" | |
class ActionItem(BaseModel): | |
id: int | |
title: str | |
description: str | |
assignee: Optional[Assignee] | |
status: str | |
def markdown(self) -> str: | |
assignee_info = self.assignee.markdown() if self.assignee else "Unassigned" | |
return f"**{self.title}** (Status: {self.status})\n" \ | |
f"- Description: {self.description}\n" \ | |
f"- Assignee: {assignee_info}\n" | |
class MeetingNotes(BaseModel): | |
id: int | |
name: str | |
createdAt: str | |
duration: int | |
url: Optional[HttpUrl] | |
recordingUrl: Optional[HttpUrl] | |
attendees: List[Attendee] | |
notes: str | |
actionItems: List[ActionItem] | |
def to_markdown(self) -> str: | |
attendees_md = "\n".join([attendee.markdown() for attendee in self.attendees]) | |
action_items_md = "\n".join([item.markdown() for item in self.actionItems]) | |
return f"# Meeting: {self.name}\n\n" \ | |
f"**Created At:** {self.createdAt}\n" \ | |
f"**Duration:** {self.duration} seconds\n" \ | |
f"**URL:** {self.url if self.url else 'N/A'}\n\n" \ | |
f"**Attendees:**\n{attendees_md}\n\n" \ | |
f"# Notes\n\n{self.notes}\n\n" \ | |
f"# Action Items\n\n{action_items_md}\n" | |
@app.function(secrets=[Secret.from_name("OPENAI_API_KEY")]) | |
@web_endpoint(method="POST") | |
async def f(request: Request): | |
body = await request.json() | |
try: | |
webhook_data = MeetingNotes(**body) | |
except Exception as e: | |
raise HTTPException(status_code=400, detail=str(e)) from e | |
transcript = webhook_data.to_markdown() | |
print('PROMPT:\n', '==='*10, '\n', prompt(transcript), '\n', '==='*10) | |
proposal = gen_proposal(transcript) | |
print('\n\nAI GENERATED PROPOSAL:\n\n', '==='*10, '\n', proposal, '\n', '==='*10) | |
def prompt(transcript: str): | |
return f""" | |
Here is an example consulting proposal: | |
<example> | |
{proposal} | |
</example> | |
Here is a transcript of a recent call with a potential client (the client name is in the transcript): | |
<transcript> | |
{transcript} | |
</transcript> | |
Write a detailed project proposal, but do not worry about the pricing or the rate. Focus on fleshing out the different tasks we would do that is compelling and convincing for a founder. | |
Flesh out these ideas and add more as necessary. Do not anchor too much on the given examples, instead write a convincing proposal. | |
""" | |
def gen_proposal(transcript: str): | |
from openai import OpenAI | |
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) | |
response = client.chat.completions.create( | |
model="gpt-4-turbo", | |
messages=[ | |
{"role": "system", "content": "You are an expert AI consultant. Your task is to write a project proposal for a potential client. The client is interested in your services and wants to know more about your approach to solving their problems. Write a detailed proposal that outlines the tasks you would do to help them grow their business. Focus on the value you can provide and how you can help them achieve their goals. Be persuasive and convincing."}, | |
{"role": "user", "content": prompt(transcript)} | |
] | |
) | |
return response.choices[0].message.content |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment