Skip to content

Commit

Permalink
Widgets (#91)
Browse files Browse the repository at this point in the history
Add examples for widgets for playground

---------

Co-authored-by: Bagatur <[email protected]>
Co-authored-by: Nuno Campos <[email protected]>
Co-authored-by: Tat Dat Duong <[email protected]>
  • Loading branch information
4 people authored Oct 26, 2023
1 parent 44dc693 commit 17eda1e
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 58 deletions.
80 changes: 80 additions & 0 deletions examples/widgets/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import base64
from typing import Any, Dict, List, Tuple

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from langchain.document_loaders.blob_loaders import Blob
from langchain.document_loaders.parsers.pdf import PDFMinerParser
from langchain.schema.runnable import RunnableLambda
from pydantic import BaseModel, Field

from langserve.server import add_routes

app = FastAPI(
title="LangChain Server",
version="1.0",
description="Spin up a simple api server using Langchain's Runnable interfaces",
)


# Set all CORS enabled origins
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)


class ChatHistory(BaseModel):
chat_history: List[Tuple[str, str]] = Field(
...,
examples=[[("a", "aa")]],
extra={"widget": {"type": "chat", "input": "question", "output": "answer"}},
)
question: str


class FileProcessingRequest(BaseModel):
file: bytes = Field(..., extra={"widget": {"type": "base64file"}})
num_chars: int = 100


def chat_with_bot(input: Dict[str, Any]) -> Dict[str, Any]:
"""Bot that repeats the question twice."""
return {
"answer": input["question"] * 2,
"woof": "its so bad to woof, meow is better",
}


def process_file(input: Dict[str, Any]) -> str:
"""Extract the text from the first page of the PDF."""
content = base64.decodebytes(input["file"])
blob = Blob(data=content)
documents = list(PDFMinerParser().lazy_parse(blob))
content = documents[0].page_content
return content[: input["num_chars"]]


add_routes(
app,
RunnableLambda(chat_with_bot).with_types(input_type=ChatHistory),
config_keys=["configurable"],
path="/chat",
)

add_routes(
app,
RunnableLambda(process_file).with_types(input_type=FileProcessingRequest),
config_keys=["configurable"],
path="/pdf",
)


if __name__ == "__main__":
import uvicorn

uvicorn.run(app, host="localhost", port=8000)

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion langserve/playground/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<link rel="icon" href="/____LANGSERVE_BASE_URL/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Playground</title>
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-0ea7a041.js"></script>
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-d732a22e.js"></script>
<link rel="stylesheet" href="/____LANGSERVE_BASE_URL/assets/index-da983f33.css">
</head>
<body>
Expand Down
5 changes: 5 additions & 0 deletions langserve/playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ import {
ChatMessageTuplesControlRenderer,
chatMessagesTupleTester,
} from "./components/ChatMessageTuplesControlRenderer";
import {
fileBase64Tester,
FileBase64ControlRenderer,
} from "./components/FileBase64Tester";

dayjs.extend(relativeDate);
dayjs.extend(utc);
Expand Down Expand Up @@ -110,6 +114,7 @@ const renderers = [
tester: chatMessagesTupleTester,
renderer: ChatMessageTuplesControlRenderer,
},
{ tester: fileBase64Tester, renderer: FileBase64ControlRenderer },
];

const nestedArrayControlTester: RankedTester = rankWith(1, (_, jsonSchema) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
isControl,
} from "@jsonforms/core";
import { AutosizeTextarea } from "./AutosizeTextarea";
import { isJsonSchemaExtra } from "../utils/schema";

type MessageTuple = [string, string];

Expand All @@ -21,15 +22,18 @@ export const chatMessagesTupleTester = rankWith(
if (typeof schema.items !== "object" || schema.items == null)
return false;

if (!isJsonSchemaExtra(schema) || schema.extra.widget.type !== "chat") {
return false;
}

if ("type" in schema.items) {
return (
schema.items.type === "array" &&
schema.items.minItems === 2 &&
schema.items.maxItems === 2 &&
Array.isArray(schema.items.items) &&
schema.items.items.length === 2 &&
schema.items.items.every((schema) => schema.type === "string") &&
schema.title === "Chat History"
schema.items.items.every((schema) => schema.type === "string")
);
}

Expand Down
44 changes: 44 additions & 0 deletions langserve/playground/src/components/FileBase64Tester.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ChangeEvent } from "react";
import { withJsonFormsControlProps } from "@jsonforms/react";
import { rankWith, and, schemaMatches, isControl } from "@jsonforms/core";
import { isJsonSchemaExtra } from "../utils/schema";

export const fileBase64Tester = rankWith(
12,
and(
isControl,
schemaMatches((schema) => {
if (!isJsonSchemaExtra(schema)) return false;
return schema.extra.widget.type === "base64file";
})
)
);

export const FileBase64ControlRenderer = withJsonFormsControlProps((props) => {
const handleFileUpload = (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;

const reader = new FileReader();

reader.onload = () => {
const base64String = reader.result as string | null;
if (base64String != null) {
const prefix = base64String.indexOf("base64,") + "base64,".length;
props.handleChange(props.path, base64String.slice(prefix));
}
};

reader.readAsDataURL(file);
};

return (
<div className="control">
<label className="text-xs uppercase font-semibold text-ls-gray-100">
{props.label}
</label>

<input type="file" onChange={handleFileUpload} />
</div>
);
});
27 changes: 27 additions & 0 deletions langserve/playground/src/utils/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { JsonSchema } from "@jsonforms/core";

type JsonSchemaExtra = JsonSchema & {
extra: {
widget: {
type: string;
};
};
};

export function isJsonSchemaExtra(x: JsonSchema): x is JsonSchemaExtra {
if (!("extra" in x && typeof x.extra === "object" && x.extra != null)) {
return false;
}

if (
!(
"widget" in x.extra &&
typeof x.extra.widget === "object" &&
x.extra.widget != null
)
) {
return false;
}

return true;
}

0 comments on commit 17eda1e

Please sign in to comment.