Spaces:
Runtime error
Runtime error
Create presentation_api.py
Browse files- presentation_api.py +130 -0
presentation_api.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
+
from fastapi import FastAPI
|
| 3 |
+
from fastapi.responses import StreamingResponse
|
| 4 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 5 |
+
from langchain_core.messages import BaseMessage, HumanMessage, trim_messages
|
| 6 |
+
from langchain_core.tools import tool
|
| 7 |
+
from langchain_openai import ChatOpenAI
|
| 8 |
+
from langgraph.checkpoint.memory import MemorySaver
|
| 9 |
+
from langgraph.prebuilt import create_react_agent
|
| 10 |
+
from pydantic import BaseModel
|
| 11 |
+
from typing import Optional
|
| 12 |
+
import json
|
| 13 |
+
from sse_starlette.sse import EventSourceResponse
|
| 14 |
+
from datetime import datetime
|
| 15 |
+
from fastapi import APIRouter
|
| 16 |
+
|
| 17 |
+
router = APIRouter(
|
| 18 |
+
prefix="/presentation",
|
| 19 |
+
tags=["presentation"]
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@tool
|
| 24 |
+
def plan(input: dict) -> str:
|
| 25 |
+
"""Create a presentation plan with numbered slides and their descriptions.
|
| 26 |
+
Args:
|
| 27 |
+
input: Dictionary containing presentation details
|
| 28 |
+
Returns:
|
| 29 |
+
A dictionary with slide numbers as keys and descriptions as values
|
| 30 |
+
"""
|
| 31 |
+
return "plan created"
|
| 32 |
+
|
| 33 |
+
@tool
|
| 34 |
+
def create_slide(slideno: int, content: str) -> str:
|
| 35 |
+
"""Create a single presentation slide.
|
| 36 |
+
Args:
|
| 37 |
+
slideno: The slide number to create
|
| 38 |
+
content: The content for the slide
|
| 39 |
+
Returns:
|
| 40 |
+
Confirmation of slide creation
|
| 41 |
+
"""
|
| 42 |
+
return f"slide {slideno} created"
|
| 43 |
+
|
| 44 |
+
memory = MemorySaver()
|
| 45 |
+
model = ChatOpenAI(model="gpt-4-turbo-preview", streaming=True)
|
| 46 |
+
prompt = ChatPromptTemplate.from_messages([
|
| 47 |
+
("system", """You are a Presentation Creation Assistant. Your task is to help users create effective presentations.
|
| 48 |
+
Follow these steps:
|
| 49 |
+
1. First use the plan tool to create an outline of the presentation
|
| 50 |
+
2. Then use create_slide tool for each slide in sequence
|
| 51 |
+
3. Guide the user through the presentation creation process
|
| 52 |
+
|
| 53 |
+
Today's date is {datetime.now().strftime('%Y-%m-%d')}"""),
|
| 54 |
+
("placeholder", "{messages}"),
|
| 55 |
+
])
|
| 56 |
+
|
| 57 |
+
def state_modifier(state) -> list[BaseMessage]:
|
| 58 |
+
try:
|
| 59 |
+
formatted_prompt = prompt.invoke({
|
| 60 |
+
"messages": state["messages"]
|
| 61 |
+
})
|
| 62 |
+
return trim_messages(
|
| 63 |
+
formatted_prompt,
|
| 64 |
+
token_counter=len,
|
| 65 |
+
max_tokens=16000,
|
| 66 |
+
strategy="last",
|
| 67 |
+
start_on="human",
|
| 68 |
+
include_system=True,
|
| 69 |
+
allow_partial=False,
|
| 70 |
+
)
|
| 71 |
+
except Exception as e:
|
| 72 |
+
print(f"Error in state modifier: {str(e)}")
|
| 73 |
+
return state["messages"]
|
| 74 |
+
|
| 75 |
+
# Create the agent with presentation tools
|
| 76 |
+
agent = create_react_agent(
|
| 77 |
+
model,
|
| 78 |
+
tools=[plan, create_slide],
|
| 79 |
+
checkpointer=memory,
|
| 80 |
+
state_modifier=state_modifier,
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
class ChatInput(BaseModel):
|
| 84 |
+
message: str
|
| 85 |
+
thread_id: Optional[str] = None
|
| 86 |
+
|
| 87 |
+
@router.post("/chat")
|
| 88 |
+
async def chat(input_data: ChatInput):
|
| 89 |
+
thread_id = input_data.thread_id or str(uuid.uuid4())
|
| 90 |
+
|
| 91 |
+
config = {
|
| 92 |
+
"configurable": {
|
| 93 |
+
"thread_id": thread_id
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
input_message = HumanMessage(content=input_data.message)
|
| 98 |
+
|
| 99 |
+
async def generate():
|
| 100 |
+
async for event in agent.astream_events(
|
| 101 |
+
{"messages": [input_message]},
|
| 102 |
+
config,
|
| 103 |
+
version="v2"
|
| 104 |
+
):
|
| 105 |
+
kind = event["event"]
|
| 106 |
+
|
| 107 |
+
if kind == "on_chat_model_stream":
|
| 108 |
+
content = event["data"]["chunk"].content
|
| 109 |
+
if content:
|
| 110 |
+
yield f"{json.dumps({'type': 'token', 'content': content})}\n"
|
| 111 |
+
|
| 112 |
+
elif kind == "on_tool_start":
|
| 113 |
+
tool_input = event['data'].get('input', '')
|
| 114 |
+
yield f"{json.dumps({'type': 'tool_start', 'tool': event['name'], 'input': tool_input})}\n"
|
| 115 |
+
|
| 116 |
+
elif kind == "on_tool_end":
|
| 117 |
+
tool_output = event['data'].get('output', '')
|
| 118 |
+
yield f"{json.dumps({'type': 'tool_end', 'tool': event['name'], 'output': tool_output})}\n"
|
| 119 |
+
|
| 120 |
+
return EventSourceResponse(
|
| 121 |
+
generate(),
|
| 122 |
+
media_type="text/event-stream"
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
@app.get("/health")
|
| 126 |
+
async def health_check():
|
| 127 |
+
return {"status": "healthy"}
|
| 128 |
+
|
| 129 |
+
if __name__ == "__main__":
|
| 130 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|