Skip to content

Commit

Permalink
Start of frontend changes
Browse files Browse the repository at this point in the history
Time travel, use checkpoint as primary source of truth

Refactor state management for chat window

Add support for state graph

Fixes

Pare down unneeded functionality, frontend updates

Fix repeated history fetches

Add basic state graph support, many other fixes

Revise state graph time travel flow

Use message graph as default

Fix flashing messages in UI on send

Allow adding and deleting tool calls

Hacks!

Only accept module paths

More logs

add env

add built ui files

Build ui files

Update cli

Delete .github/workflows/build_deploy_image.yml

Update path

Update ui files

Move migrations

Move ui files

0.0.5

Allow resume execution for tool messages (#2)

Undo

Undo

Remove cli

Undo

Undo

Update storage/threads

Undo ui

Undo

Lint

Undo

Rm

Undo

Rm

Update api

Undo

WIP
  • Loading branch information
jacoblee93 authored and nfcampos committed Apr 15, 2024
1 parent 735fd45 commit 6b33fd8
Show file tree
Hide file tree
Showing 14 changed files with 1,125 additions and 114 deletions.
7 changes: 5 additions & 2 deletions backend/app/api/threads.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import asyncio
from typing import Annotated, Any, Dict, List, Sequence, Union
from typing import Annotated, Any, Dict, List, Optional, Sequence, Union
from uuid import uuid4

from fastapi import APIRouter, HTTPException, Path
Expand Down Expand Up @@ -27,6 +27,7 @@ class ThreadPostRequest(BaseModel):
"""Payload for adding state to a thread."""

values: Union[Sequence[AnyMessage], Dict[str, Any]]
config: Optional[Dict[str, Any]] = None


@router.get("/")
Expand Down Expand Up @@ -60,7 +61,9 @@ async def add_thread_state(
thread = await storage.get_thread(user["user_id"], tid)
if not thread:
raise HTTPException(status_code=404, detail="Thread not found")
return await storage.update_thread_state(user["user_id"], tid, payload.values)
return await storage.update_thread_state(
payload.config or {"configurable": {"thread_id": tid}}, payload.values
)


@router.get("/{tid}/history")
Expand Down
12 changes: 6 additions & 6 deletions backend/app/storage.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional, Sequence, Union
from typing import Any, List, Optional, Sequence, Union

from langchain_core.messages import AnyMessage
from langchain_core.runnables import RunnableConfig

from app.agent import AgentType, get_agent_executor
from app.lifespan import get_pg_pool
Expand Down Expand Up @@ -109,26 +110,25 @@ async def get_thread_state(user_id: str, thread_id: str):


async def update_thread_state(
user_id: str, thread_id: str, values: Union[Sequence[AnyMessage], Dict[str, Any]]
config: RunnableConfig, values: Union[Sequence[AnyMessage], dict[str, Any]]
):
"""Add state to a thread."""
app = get_agent_executor([], AgentType.GPT_35_TURBO, "", False)
await app.aupdate_state({"configurable": {"thread_id": thread_id}}, values)
return await app.aupdate_state(config, values)


async def get_thread_history(user_id: str, thread_id: str):
"""Get the history of a thread."""
app = get_agent_executor([], AgentType.GPT_35_TURBO, "", False)
config = {"configurable": {"thread_id": thread_id}}
return [
{
"values": c.values,
"next": c.next,
"config": c.config,
"parent": c.parent_config,
}
async for c in app.aget_state_history(
{"configurable": {"thread_id": thread_id}}
)
async for c in app.aget_state_history(config)
]


Expand Down
4 changes: 4 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "frontend",
"private": true,
"version": "0.0.0",
"packageManager": "yarn@1.22.19",
"type": "module",
"scripts": {
"dev": "vite --host",
Expand All @@ -11,9 +12,12 @@
"format": "prettier -w src"
},
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"@microsoft/fetch-event-source": "^2.0.1",
"@mui/material": "^5.15.14",
"@tailwindcss/forms": "^0.5.6",
"@tailwindcss/typography": "^0.5.10",
"clsx": "^2.0.0",
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ function App(props: { edit?: boolean }) {
const { currentChat, assistantConfig, isLoading } = useThreadAndAssistant();

const startTurn = useCallback(
async (message: MessageWithFiles | null, thread_id: string) => {
async (
message: MessageWithFiles | null,
thread_id: string,
config?: Record<string, unknown>,
) => {
const files = message?.files || [];
if (files.length > 0) {
const formData = files.reduce((formData, file) => {
Expand Down Expand Up @@ -56,6 +60,7 @@ function App(props: { edit?: boolean }) {
]
: null,
thread_id,
config,
);
},
[startStream],
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/assets/EmptyState.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions frontend/src/components/AutosizeTextarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Ref } from "react";
import { cn } from "../utils/cn";

const COMMON_CLS = cn(
"text-sm col-[1] row-[1] m-0 resize-none overflow-hidden whitespace-pre-wrap break-words bg-transparent px-2 py-1 rounded shadow-none",
);

export function AutosizeTextarea(props: {
id?: string;
inputRef?: Ref<HTMLTextAreaElement>;
value?: string | null | undefined;
placeholder?: string;
className?: string;
onChange?: (e: string) => void;
onFocus?: () => void;
onBlur?: () => void;
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
autoFocus?: boolean;
readOnly?: boolean;
cursorPointer?: boolean;
disabled?: boolean;
fullHeight?: boolean;
}) {
return (
<div
className={
cn("grid w-full", props.className) +
(props.fullHeight ? "" : " max-h-80 overflow-auto ")
}
>
<textarea
ref={props.inputRef}
id={props.id}
className={cn(
COMMON_CLS,
"text-transparent caret-black rounded focus:outline-0 focus:ring-0",
)}
disabled={props.disabled}
value={props.value ?? ""}
rows={1}
onChange={(e) => {
const target = e.target as HTMLTextAreaElement;
props.onChange?.(target.value);
}}
onFocus={props.onFocus}
onBlur={props.onBlur}
placeholder={props.placeholder}
readOnly={props.readOnly}
autoFocus={props.autoFocus && !props.readOnly}
onKeyDown={props.onKeyDown}
/>
<div
aria-hidden
className={cn(COMMON_CLS, "pointer-events-none select-none")}
>
{props.value}{" "}
</div>
</div>
);
}
14 changes: 11 additions & 3 deletions frontend/src/components/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ import { ArrowDownCircleIcon } from "@heroicons/react/24/outline";
import { MessageWithFiles } from "../utils/formTypes.ts";
import { useParams } from "react-router-dom";
import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts";
// import { useHistories } from "../hooks/useHistories.ts";
// import { Timeline } from "./Timeline.tsx";
// import { deepEquals } from "../utils/equals.ts";

interface ChatProps extends Pick<StreamStateProps, "stream" | "stopStream"> {
interface ChatProps
extends Pick<
StreamStateProps,
"stream" | "stopStream" | "streamErrorMessage"
> {
startStream: (
message: MessageWithFiles | null,
thread_id: string,
Expand Down Expand Up @@ -66,9 +73,10 @@ export function Chat(props: ChatProps) {
...
</div>
)}
{props.stream?.status === "error" && (
{(props.streamErrorMessage || props.stream?.status === "error") && (
<div className="flex items-center rounded-md bg-yellow-50 px-2 py-1 text-xs font-medium text-yellow-800 ring-1 ring-inset ring-yellow-600/20">
An error has occurred. Please try again.
{props.streamErrorMessage ??
"An error has occurred. Please try again."}
</div>
)}
{next.length > 0 && props.stream?.status !== "inflight" && (
Expand Down
Loading

0 comments on commit 6b33fd8

Please sign in to comment.