Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions backend/fastapi_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ async def get_image(prompt_dict: dict):
async def get_text(request: Request):
try:
body = await request.json()
prompt = body.get('prompt')
messages = body.get('messages')
except json.JSONDecodeError:
raise HTTPException(status_code=500, detail="Cant decode JSON")
if not prompt:
raise HTTPException(status_code=400, detail="Prompt is required")
if not messages:
raise HTTPException(status_code=400, detail="Messages list is required")

return StreamingResponse(query_synapse_text(dendrite, metagraph, subtensor, prompt), media_type='text/event-stream')
return StreamingResponse(query_synapse_text(dendrite, metagraph, subtensor, messages), media_type='text/event-stream')

6 changes: 3 additions & 3 deletions backend/neurons/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ async def main():
bt.logging.error(f"General exception at step: {e}\n{traceback.format_exc()}")


async def query_synapse_text(dendrite, metagraph, subtensor, prompt):
async def query_synapse_text(dendrite, metagraph, subtensor, messages):
try:
axon = metagraph.axons[87]
syn = StreamPrompting(messages=[{"role": "user", "content": prompt}], engine="gpt-4-1106-preview", seed=1234)
syn = StreamPrompting(messages=messages, engine="gpt-4-1106-preview", seed=1234)

responses = await dendrite([axon], syn, deserialize=False, streaming=True)
for resp in responses:
Expand All @@ -61,7 +61,7 @@ async def query_synapse_text(dendrite, metagraph, subtensor, prompt):

def main():
config, wallet, subtensor, dendrite, metagraph = initialize()
# asyncio.run(query_synapse_text(dendrite, metagraph, subtensor, "tell me a story"))
# asyncio.run(query_synapse_text(dendrite, metagraph, subtensor, [{"role": "user", "content": "tell me a story"}]))
asyncio.run(query_synapse_image(dendrite, metagraph, subtensor, "dragon"))


Expand Down
Binary file modified web/public/favicon.ico
Binary file not shown.
Binary file added web/public/taotensor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/taotensor_logo.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
279 changes: 279 additions & 0 deletions web/public/taotensor_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions web/src/components/common/ImagePreview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { CSSProperties, useState } from "react";
import {
Button,
Dialog,
Expand All @@ -12,9 +12,10 @@ import { Close as CloseIcon } from "@mui/icons-material";

interface ImagePreviewProps {
imageUrl: string;
style?: CSSProperties | undefined;
}

const ImagePreview: React.FC<ImagePreviewProps> = ({ imageUrl }) => {
const ImagePreview: React.FC<ImagePreviewProps> = ({ imageUrl, style }) => {
const [open, setOpen] = useState(false);

const handleOpen = () => {
Expand All @@ -26,7 +27,7 @@ const ImagePreview: React.FC<ImagePreviewProps> = ({ imageUrl }) => {
};

return (
<div>
<div style={style}>
<img
src={imageUrl}
alt="Preview"
Expand Down
6 changes: 5 additions & 1 deletion web/src/components/pages/chatPage/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ const ChatWindow: React.FC = () => {
messages[messages.length - 1]?.type === "text-loading"
) {
fetchTextMessage(
messages[messages.length - 2].text,
messages.slice(0, -1).map((message) => (
{
role: message.author === "user" ? "user" : "assistant",
content: message.text
})),
onopen,
onmessage,
onerror,
Expand Down
12 changes: 10 additions & 2 deletions web/src/components/pages/chatPage/InputBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useRef, useEffect } from "react";
import { Box, TextField, IconButton, MenuItem, Select } from "@mui/material";
import SendIcon from "@mui/icons-material/Send";

Expand All @@ -16,6 +16,7 @@ const InputBar: React.FC<InputBarProps> = ({
setMode,
}) => {
const [message, setMessage] = useState("");
const inputRef = useRef<HTMLInputElement>(null);

const handleSend = () => {
if (message.trim()) {
Expand All @@ -31,6 +32,12 @@ const InputBar: React.FC<InputBarProps> = ({
}
};

useEffect(() => {
if (inputRef.current && enabled) {
inputRef.current.focus();
}
})

return (
<Box
sx={{
Expand All @@ -45,7 +52,7 @@ const InputBar: React.FC<InputBarProps> = ({
<Select
onChange={(e) => setMode(e.target.value as "text" | "image")}
value={mode}
sx={{ minWidth: "7vw" }}
sx={{ minWidth: "100px" }}
>
<MenuItem value={"text"}>Text</MenuItem>
<MenuItem value={"image"}>Image</MenuItem>
Expand All @@ -57,6 +64,7 @@ const InputBar: React.FC<InputBarProps> = ({
onKeyDown={handleKeyDown}
placeholder="Type a message..."
disabled={!enabled}
inputRef={inputRef}
sx={{
"& .MuiOutlinedInput-root": {
"&.Mui-focused fieldset": {
Expand Down
33 changes: 26 additions & 7 deletions web/src/components/pages/chatPage/MessageItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { Box, Skeleton, Typography } from "@mui/material";
import { Avatar, Box, Skeleton, Typography } from "@mui/material";
import Message from "../../../types/Message";
import ImagePreview from "../../common/ImagePreview";

Expand All @@ -11,24 +11,40 @@ const MessageItem: React.FC<MessageItemProps> = ({ message }) => {
let contentElem;
switch (message.type) {
case "text":
if (message.text === "") {
contentElem = (
<div style={{ display: "flex", alignItems: "center"}}>
<Skeleton variant="circular" width={10} height={10} sx={{ transform: "none", marginLeft: "50px" }} />
<Skeleton variant="circular" width={10} height={10} sx={{ transform: "none", marginLeft: "5px", animationDelay: 0.2 }} />
<Skeleton variant="circular" width={10} height={10} sx={{ transform: "none", marginLeft: "5px", animationDelay: 0.4 }} />
</div>
)
break;
}
contentElem = (
<Typography sx={{ wordBreak: "break-word" }}>{message.text}</Typography>
<Typography sx={{ wordBreak: "break-word", marginLeft: "50px" }}>{message.text}</Typography>
);
break;
case "image":
contentElem = <ImagePreview imageUrl={message.text} />;
contentElem = <ImagePreview imageUrl={message.text} style={{marginLeft: "50px"}} />;
break;
case "image-loading":
contentElem = (
<Skeleton width={"50%"} height={"40vh"} sx={{ transform: "none" }} />
<Skeleton width={"50%"} height={"40vh"} sx={{ transform: "none", textImarginLeftndent: "50px", marginLeft: "50px" }} />
);
break;
case "text-loading":
contentElem = <Skeleton width={"50%"} sx={{ transform: "none" }} />;
contentElem = (
<div style={{ display: "flex", alignItems: "center"}}>
<Skeleton variant="circular" width={10} height={10} sx={{ transform: "none", marginLeft: "50px" }} />
<Skeleton variant="circular" width={10} height={10} sx={{ transform: "none", marginLeft: "5px", animationDelay: 0.2 }} />
<Skeleton variant="circular" width={10} height={10} sx={{ transform: "none", marginLeft: "5px", animationDelay: 0.4 }} />
</div>
)
break;
case "error":
contentElem = (
<Typography sx={{ wordBreak: "break-word" }} color={"error"}>
<Typography sx={{ wordBreak: "break-word", marginLeft: "50px" }} color={"error"}>
{message.text}
</Typography>
);
Expand All @@ -39,7 +55,10 @@ const MessageItem: React.FC<MessageItemProps> = ({ message }) => {
marginBottom: "16px",
}}
>
<Typography fontWeight={"bold"}>{message.author}</Typography>
<div style={{ display: "flex", alignItems: "center"}}>
<Avatar src={message.author === 'bot' ? "/taotensor.png" : ""} imgProps={{ style: {objectFit: 'contain'}}} sx={{ width: 40, height: 40, marginRight: "8px" }}/>
<Typography fontWeight={"bold"}>{message.author === 'user' ? 'You' : 'Chatbot' }</Typography>
</div>
{contentElem}
</Box>
);
Expand Down
7 changes: 6 additions & 1 deletion web/src/components/pages/chatPage/MessagesContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Message from "../../../types/Message";
import MessageItem from "./MessageItem";
import { Box, IconButton } from "@mui/material";
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
import NoMessages from "./NoMessages";

interface MessagesContainerProps {
messages: Message[];
Expand Down Expand Up @@ -36,11 +37,15 @@ const MessagesContainer: React.FC<MessagesContainerProps> = ({ messages }) => {
padding: "16px",
}}
>
<Box sx={{ overflow: "auto", flexGrow: 1, padding: "10px" }}>
{ messages && messages.length ? (
<Box sx={{ overflow: "auto", flexGrow: 1, padding: "10px", position:"relative" }}>
{messages.map((message, index) => (
<MessageItem key={index} message={message} />
))}
</Box>
) : (
<NoMessages />
)}
{showScrollButton && (
<IconButton
onClick={scrollToBottom}
Expand Down
15 changes: 15 additions & 0 deletions web/src/components/pages/chatPage/NoMessages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FC } from "react";
import { Container, Avatar } from "@mui/material";

const NoMessages: FC = () => {
return (
<Container sx={{ height: "100%", display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column" }}>
<Avatar src="/taotensor_logo.svg" sx={{ width: 200, height: 200}}/>
<p style={{ fontSize: "1.5rem", fontWeight: "bold", textAlign: "center", marginTop: 0 }}>
How can I help you?
</p>
</Container>
);
}

export default NoMessages;
7 changes: 5 additions & 2 deletions web/src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,25 @@ import {
fetchEventSource,
} from "@microsoft/fetch-event-source";

import { OpenAIMessage } from "../types/OpenAIMessage";

export const BACKEND_BASE_URL = process.env.REACT_APP_BACKEND_HOST;
export const generateImage = (prompt: string) => {
return axios.post(`${BACKEND_BASE_URL}/generate-image`, { prompt: prompt });
};

export const fetchTextMessage = async (
prompt: string,
messages: Array<OpenAIMessage>,
onopen: (res: Response) => void,
onmessage: (event: EventSourceMessage) => void,
onerror: (err: any) => void,
onclose: () => void,
) => {
await fetchEventSource(`${BACKEND_BASE_URL}/generate-text`, {
openWhenHidden: true,
method: "POST",
headers: { Accept: "text/event-stream" },
body: JSON.stringify({ prompt: prompt }),
body: JSON.stringify({ messages: messages }),
// @ts-ignore
onopen(res) {
onopen(res);
Expand Down
4 changes: 4 additions & 0 deletions web/src/types/OpenAIMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface OpenAIMessage {
role: string;
content: string;
}