Skip to content

Commit

Permalink
Add /playground endpoint that serves an playground UI with support fo…
Browse files Browse the repository at this point in the history
…r configuration and intermediate steps
  • Loading branch information
nfcampos committed Oct 5, 2023
1 parent 4609dbc commit 39650ec
Show file tree
Hide file tree
Showing 25 changed files with 2,662 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_ENDPOINT=http://localhost:8000
export OPENAI_API_KEY=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
21 changes: 16 additions & 5 deletions examples/chain/server.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
#!/usr/bin/env python
"""Example LangChain server exposes a chain composed of a prompt and an LLM."""
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from typing_extensions import TypedDict

from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate, ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable.utils import ConfigurableField
from langserve import add_routes

model = ChatOpenAI().configurable_alternatives(
model = ChatOpenAI(temperature=0.5).configurable_alternatives(
ConfigurableField(id="llm", name="LLM"),
high_temp=ChatOpenAI(temperature=0.9),
low_temp=ChatOpenAI(temperature=0.1, max_tokens=1),
mid_temp=ChatOpenAI(temperature=0.5),
)

# prompt = ChatPromptTemplate.from_messages(
Expand All @@ -24,18 +25,28 @@
"tell me a joke about {topic}"
).configurable_fields(
template=ConfigurableField(
id="topic", name="Topic", description="The topic of the joke"
id="prompt", name="Prompt", description="The prompt to use."
)
)

chain = prompt | model
chain = prompt | model | StrOutputParser()

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=["*"],
)


# The input type is automatically inferred from the runnable
# interface; however, if you want to override it, you can do so
Expand All @@ -56,4 +67,4 @@ class ChainInput(TypedDict):
if __name__ == "__main__":
import uvicorn

uvicorn.run(app, host="localhost", port=8000)
uvicorn.run(app, host="localhost", port=8003)
18 changes: 18 additions & 0 deletions langserve-playground/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
24 changes: 24 additions & 0 deletions langserve-playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
27 changes: 27 additions & 0 deletions langserve-playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
21 changes: 21 additions & 0 deletions langserve-playground/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Playground</title>
</head>
<body>
<div id="root"></div>
<script>
try {
window.CONFIG_SCHEMA = ____LANGSERVE_CONFIG_SCHEMA
window.INPUT_SCHEMA = ____LANGSERVE_INPUT_SCHEMA
} catch (error) {
// pass
}
</script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
39 changes: 39 additions & 0 deletions langserve-playground/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "langserve-playground",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@jsonforms/core": "^3.1.0",
"@jsonforms/material-renderers": "^3.1.0",
"@jsonforms/react": "^3.1.0",
"@microsoft/fetch-event-source": "^2.0.1",
"@mui/icons-material": "^5.14.11",
"@mui/material": "^5.14.11",
"@mui/x-date-pickers": "^6.16.0",
"fast-json-patch": "^3.1.1",
"json-schema-defaults": "^0.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.3",
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"typescript": "^5.0.2",
"vite": "^4.4.5"
}
}
1 change: 1 addition & 0 deletions langserve-playground/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions langserve-playground/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
}
127 changes: 127 additions & 0 deletions langserve-playground/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React, { useEffect, useState } from "react";
import { Card, Button, Stack } from "@mui/material";
import defaults from "json-schema-defaults";
import { JsonForms } from "@jsonforms/react";
import {
materialRenderers,
materialCells,
} from "@jsonforms/material-renderers";

import { useSchemas } from "./useSchemas";
import "./App.css";
import { useStreamLog } from "./useStreamLog";
import { JsonFormsCore } from "@jsonforms/core";

function str(o: unknown): React.ReactNode {
return typeof o === "object"
? JSON.stringify(o, null, 2)
: (o as React.ReactNode);
}

function App() {
// store form state
const [configData, setConfigData] = useState<
Pick<JsonFormsCore, "data" | "errors">
>({ data: {}, errors: [] });
const [inputData, setInputData] = useState<
Pick<JsonFormsCore, "data" | "errors">
>({ data: {}, errors: [] });
// fetch input and config schemas from the server
const schemas = useSchemas();
// apply defaults defined in each schema
useEffect(() => {
if (schemas.config) {
setConfigData({ data: defaults(schemas.config), errors: [] });
setInputData({ data: defaults(schemas.input), errors: [] });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [schemas.config]);
// the runner
const { startStream, stopStream, latest } = useStreamLog();

return schemas.config && schemas.input ? (
<>
<h1>Playground</h1>
<Card sx={{ padding: 2 }}>
<h2>Configuration</h2>
<JsonForms
schema={schemas.config}
data={configData.data}
renderers={materialRenderers}
cells={materialCells}
onChange={({ data, errors }) =>
data ? setConfigData({ data, errors }) : undefined
}
/>
{!!configData.errors?.length && (
<>
<h3>Validation Errors</h3>
{configData.errors?.map((e, i) => (
<p key={i}>{e.message}</p>
))}
</>
)}
</Card>
<Card sx={{ marginTop: 2, padding: 2 }}>
<h2>Inputs</h2>
<JsonForms
schema={schemas.input}
data={inputData.data}
renderers={materialRenderers}
cells={materialCells}
onChange={({ data, errors }) => setInputData({ data, errors })}
/>
{!!inputData.errors?.length && (
<>
<h3>Validation Errors</h3>
{inputData.errors?.map((e, i) => (
<p key={i}>{e.message}</p>
))}
</>
)}
</Card>
<Stack direction="row" spacing={2} sx={{ marginTop: 2 }}>
<Button
disabled={
!!inputData.errors?.length ||
!!configData.errors?.length ||
!!stopStream
}
variant="contained"
color="primary"
onClick={() => startStream(inputData.data, configData.data)}
>
{stopStream ? "Running..." : "Run"}
</Button>
<Button
disabled={!stopStream}
variant="contained"
color="secondary"
onClick={stopStream}
>
Stop
</Button>
</Stack>
{latest && (
<Card sx={{ marginTop: 2, padding: 2 }}>
<h2>Output</h2>
{latest.streamed_output.map(str).join("") ?? "..."}
</Card>
)}
{latest && (
<Card sx={{ marginTop: 2, padding: 2 }}>
<h2>Intermediate Steps</h2>
{Object.values(latest.logs).map((log) => (
<div key={log.id}>
<h3>{log.name}</h3>
<p>{log.start_time}</p>
<pre>{str(log.final_output) ?? "..."}</pre>
</div>
))}
</Card>
)}
</>
) : null;
}

export default App;
1 change: 1 addition & 0 deletions langserve-playground/src/assets/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 39650ec

Please sign in to comment.