code-chat-api / presentation_api.py
pvanand's picture
Update artifacts
0d7396d verified
raw
history blame
5.39 kB
import uuid
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from langchain_core.messages import BaseMessage, HumanMessage, trim_messages
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent
from pydantic import BaseModel
from typing import Optional
import json
from sse_starlette.sse import EventSourceResponse
from datetime import datetime
from fastapi import APIRouter
from langchain_core.runnables import RunnableConfig
from langchain_core.prompts import ChatPromptTemplate
from typing import Any
router = APIRouter(
prefix="/presentation",
tags=["presentation"]
)
import json
@tool(response_format="content_and_artifact")
def plan(slides_json: str) -> tuple[str, dict]:
"""Create a presentation plan from a JSON string of slides (keys=slide numbers, values=content)."""
try:
slides = json.loads(slides_json)
print(slides)
return (
f"Plan created with {len(slides)} slides: {', '.join(slides.keys())}.",
{"slides_plan_json": slides_json}
)
except Exception as e:
return (
f"Invalid JSON format. Please provide a valid JSON string {str(e)[:100]}.",
None
)
@tool(response_format="content_and_artifact")
def create_slide(slide_number: int, content: str, config: RunnableConfig) -> tuple[str, dict]:
"""Create a slide with the given number and content."""
# Integration with slide creation API or template would go here
slide = {
"number": slide_number,
"content": content,
"created_at": datetime.now().isoformat()
}
return (
f"Slide {slide_number} created",
{"slide": slide}
)
@tool(parse_docstring=True)
def execute_python(expression: str) -> str:
"""Execute a python mathematic expression. Returns the result of the expression or an error message if execution fails.
Args:
expression: The python expression to execute.
"""
try:
result = eval(expression)
return f"The result of the expression is {result}"
except Exception as e:
return f"Error executing the expression: {str(e)}"
memory = MemorySaver()
model = ChatOpenAI(model="gpt-4o-mini", streaming=True)
prompt = ChatPromptTemplate.from_messages([
("system", """You are a Presentation Creation Assistant. Your task is to help users create effective presentations.
Follow these steps:
1. First use the plan tool to create an outline of the presentation
2. Then use create_slide tool for each slide in sequence
3. Guide the user through the presentation creation process
Today's date is {{datetime.now().strftime('%Y-%m-%d')}}"""),
("placeholder", "{messages}"),
])
def state_modifier(state) -> list[BaseMessage]:
try:
formatted_prompt = prompt.invoke({
"messages": state["messages"]
})
return trim_messages(
formatted_prompt,
token_counter=len,
max_tokens=16000,
strategy="last",
start_on="human",
include_system=True,
allow_partial=False,
)
except Exception as e:
print(f"Error in state modifier: {str(e)}")
return state["messages"]
# Create the agent with presentation tools
agent = create_react_agent(
model,
tools=[plan, create_slide, execute_python],
checkpointer=memory,
state_modifier=state_modifier,
)
class ChatInput(BaseModel):
message: str
thread_id: Optional[str] = None
@router.post("/chat")
async def chat(input_data: ChatInput):
thread_id = input_data.thread_id or str(uuid.uuid4())
config = {
"configurable": {
"thread_id": thread_id
}
}
input_message = HumanMessage(content=input_data.message)
async def generate():
async for event in agent.astream_events(
{"messages": [input_message]},
config,
version="v2"
):
kind = event["event"]
if kind == "on_chat_model_stream":
content = event["data"]["chunk"].content
if content:
yield f"{json.dumps({'type': 'token', 'content': content})}\n"
elif kind == "on_tool_start":
tool_input = str(event['data'].get('input', ''))
yield f"{json.dumps({'type': 'tool_start', 'tool': event['name'], 'input': tool_input})}\n"
elif kind == "on_tool_end":
print(event['data'])
tool_output = event['data'].get('output', '')
artifact_output = tool_output.artifact if tool_output.artifact else None
yield f"{json.dumps({'type': 'tool_end', 'tool': event['name'], 'output': tool_output.pretty_repr(), 'artifacts_data': artifact_output})}\n"
print(tool_output.pretty_repr())
return EventSourceResponse(
generate(),
media_type="text/event-stream"
)
@router.get("/health")
async def health_check():
return {"status": "healthy"}
app = FastAPI()
app.include_router(router)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)