Skip to content

Commit

Permalink
wip websockets (#8)
Browse files Browse the repository at this point in the history
* wip websockets

* wip

* wip 2

* yayy

* yay 2
  • Loading branch information
BrianP8701 authored Sep 28, 2024
1 parent aa2b0a6 commit 9f4cf9d
Show file tree
Hide file tree
Showing 55 changed files with 692 additions and 274 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ server/secrets.json
**/yarn.lock
.env.prod
.env

client/graphql.schema.json
1 change: 1 addition & 0 deletions client/codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ schema: "../server/src/graphql/schema.gql"
documents:
- "src/graphql/queries.gql"
- "src/graphql/fragments.gql"
- "src/graphql/subscriptions.gql"
generates:
src/graphql/generated/graphql.ts:
plugins:
Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"pre": "yarn lint && yarn build"
},
"dependencies": {
"@apollo/client": "^3.11.4",
"@apollo/client": "^3.11.8",
"@clerk/clerk-react": "^5.4.2",
"@googlemaps/js-api-loader": "^1.16.6",
"@radix-ui/react-checkbox": "^1.0.4",
Expand Down
2 changes: 1 addition & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { ApolloClientProvider } from "./providers/ApolloClientProvider";
import MainLayout from "@/components/layouts/MainLayout";
import AuthWrapper from "./components/custom/AuthWrapper";


const App = () => {
return (
<BrowserRouter>
Expand All @@ -32,6 +31,7 @@ const Providers = (props: PropsWithChildren<NonNullable<unknown>>) => {
<ApolloClientProvider url={import.meta.env.VITE_GRAPHQL_URL}>
<TooltipProvider>
<MainLayout>
{/* Remove the WebSocketHandler component from here */}
{props.children}
</MainLayout>
</TooltipProvider>
Expand Down
8 changes: 4 additions & 4 deletions client/src/graphql/queries.gql
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ query FetchUser {
}
}

query FetchSwarm($id: ID!) {
fetchSwarm(id: $id) {
query FetchSwarm($swarmId: ID!) {
fetchSwarm(swarmId: $swarmId) {
...SwarmWithData
}
}

query FetchChat($id: ID!) {
fetchChat(id: $id) {
query FetchChat($chatId: ID!) {
fetchChat(chatId: $chatId) {
...ChatWithData
}
}
Expand Down
5 changes: 5 additions & 0 deletions client/src/graphql/subscriptions.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
subscription NewMessage($chatId: ID!) {
messageSent(chatId: $chatId) {
...Message
}
}
58 changes: 22 additions & 36 deletions client/src/providers/ApolloClientProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { PropsWithChildren, useCallback, useMemo } from 'react'

import { ApolloClient, ApolloProvider, createHttpLink, from, InMemoryCache } from '@apollo/client'
import { ApolloClient, ApolloProvider, createHttpLink, from, InMemoryCache, split } from '@apollo/client'
import { onError } from '@apollo/client/link/error'
import { setContext } from '@apollo/client/link/context'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { split } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';

import { getMainDefinition } from '@apollo/client/utilities'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { createClient } from 'graphql-ws'
import { useAuth } from '@clerk/clerk-react'

interface Props {
Expand All @@ -16,12 +13,25 @@ interface Props {

export const ApolloClientProvider = ({ children, url }: PropsWithChildren<Props>) => {
const { getToken, isSignedIn } = useAuth()

const httpLink = useMemo(() => createHttpLink({ uri: url, credentials: 'include' }), [url])

const wsLink = new GraphQLWsLink(createClient({
url: url.replace(/^http/, 'ws'),
connectionParams: async () => {
if (isSignedIn) {
const token = await getToken()
return {
Authorization: `Bearer ${token}`,
}
}
return {}
}
}))

const getConnectionContext = useCallback(async () => {
if (isSignedIn) {
try {
// Fetch a new token for each request
const token = await getToken()
return {
headers: {
Expand All @@ -44,45 +54,21 @@ export const ApolloClientProvider = ({ children, url }: PropsWithChildren<Props>
}
})

const wsLink = new GraphQLWsLink(createClient({
url: url.replace('http', 'ws'),
connectionParams: async () => {
if (isSignedIn) {
const token = await getToken();
return { Authorization: `Bearer ${token}` };
}
return {};
},
retryAttempts: 5,
retryWait: (retries) => new Promise((resolve) => setTimeout(resolve, retries * 1000)),
shouldRetry: (error: unknown) => {
const err = error as { message: string };
return !err.message.includes('Unauthorized') && !err.message.includes('Invalid token');
},
}));

const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
)
},
wsLink,
httpLink,
);
)

const client = new ApolloClient({
link: from([authLink, errorLink, splitLink]),
uri: url,
cache: new InMemoryCache({
typePolicies: {
StripeQuery: { keyFields: [] },
NoticeQuery: { keyFields: [] },
User: { keyFields: [] },
},
}),
cache: new InMemoryCache(),
connectToDevTools: true,
})

Expand Down
60 changes: 47 additions & 13 deletions client/src/views/Chat/ChatMessages.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { useEffect, useRef, useState } from "react";
import { MessageFragment, MessageRoleEnum, useFetchChatLazyQuery } from "@/graphql/generated/graphql";
import { useEffect, useRef } from "react";
import {
MessageRoleEnum,
NewMessageDocument,
useFetchChatQuery,
NewMessageSubscription,
FetchChatQuery,
} from "@/graphql/generated/graphql";

interface UserMessageProps {
content: string;
Expand Down Expand Up @@ -32,29 +38,57 @@ interface ChatMessagesProps {
isDialogMode?: boolean;
}

export function ChatMessages({selectedChatId, isDialogMode }: ChatMessagesProps) {
export function ChatMessages({ selectedChatId, isDialogMode }: ChatMessagesProps) {
const messagesEndRef = useRef<HTMLDivElement>(null)
const [messages, setMessages] = useState<MessageFragment[]>([])
const [fetchChat, { data: chatData }] = useFetchChatLazyQuery()

const { data, subscribeToMore } = useFetchChatQuery({
variables: { chatId: selectedChatId ?? '' },
skip: !selectedChatId,
});

useEffect(() => {
if (selectedChatId) {
fetchChat({ variables: { id: selectedChatId } })
}
}, [selectedChatId, fetchChat]);
const subscribeToNewMessages = subscribeToMore<NewMessageSubscription>({
document: NewMessageDocument,
variables: { chatId: selectedChatId ?? '' },
updateQuery: (prev: FetchChatQuery, { subscriptionData }) => {
if (!subscriptionData.data?.messageSent) return prev;
const newMessage = subscriptionData.data.messageSent;

useEffect(() => {
if (chatData?.fetchChat) {
setMessages(chatData.fetchChat.data?.messages ?? [])
return {
...prev,
fetchChat: prev.fetchChat
? {
...prev.fetchChat,
data: prev.fetchChat.data
? {
...prev.fetchChat.data,
messages: [
...(prev.fetchChat.data.messages?.filter(Boolean) || []),
newMessage,
].filter((message): message is NonNullable<typeof message> =>
message !== null && message !== undefined
),
}
: null,
}
: null,
};
},
});

return () => subscribeToNewMessages();
}
}, [chatData])
}, [selectedChatId, subscribeToMore]);

useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
}, [messages])
}, [data?.fetchChat?.data?.messages])

const heightClass = isDialogMode ? "h-[calc(100vh-163px)]" : "h-[calc(100vh-200px)]"

const messages = data?.fetchChat?.data?.messages || [];

return (
<div className={`flex flex-col justify-start overflow-y-auto mt-10 gap-4 ${heightClass}`}>
{messages.map((message) => (
Expand Down
2 changes: 1 addition & 1 deletion client/src/views/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function HomePage() {

useEffect(() => {
if (selectedSwarmId) {
fetchSwarm({ variables: { id: selectedSwarmId } });
fetchSwarm({ variables: { swarmId: selectedSwarmId } });
}
}, [selectedSwarmId, fetchSwarm]);

Expand Down
8 changes: 8 additions & 0 deletions docs/references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
https://www.cursor.com/blog/problems-2023

https://www.cursor.com/blog/problems-2024

https://www.cursor.com/blog/shadow-workspace

https://honeycomb.sh/blog/swe-bench-technical-report

9 changes: 8 additions & 1 deletion infra/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
version: '3.9'
services:
postgres:
postgres_swarmstar:
image: postgres:16-alpine
container_name: postgres_swarmstar
ports:
- "5434:5432"
volumes:
Expand All @@ -10,7 +11,13 @@ services:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: swarmstarv2
networks:
- swarmstar_network

volumes:
swarmstarv2:
driver: local

networks:
swarmstar_network:
driver: bridge
19 changes: 18 additions & 1 deletion infra/local-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,21 @@

```bash
cd infra && docker-compose up -d
```
```

2. Install dependencies

```bash
yarn
```

3. Start the server

```bash
yarn server
```

3. Start the client
```bash
yarn client
```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"scripts": {
"client": "cd client && yarn start",
"server": "cd server && yarn start:dev",
"ws": "cd server && yarn start:dev:ws",
"dev": "concurrently \"yarn client\" \"yarn server\"",
"lint:python": "cd swarmstar && yarn lint:python",
"generate": "yarn server:generate && yarn client:generate",
Expand Down
3 changes: 2 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"cli": "ts-node src/cli/index.ts",
"generate-action-metadata": "ts-node src/cli/generateActionMetadata.ts",
"start:dev": "nodemon src/server.ts",
"start:dev": "nodemon src/graphql-server.ts",
"start:dev:ws": "nodemon src/websocket-server.ts",
"build": "tsc --project ./ && yarn copy-schemas",
"copy-schemas": "npx cpx -v \"src/**/*.gql\" dist && cp package.json dist/package.json && cp yarn.lock dist/yarn.lock && mkdir -p dist/prisma && cp -r prisma/schema.prisma dist/prisma/schema.prisma",
Expand Down Expand Up @@ -58,6 +58,7 @@
"dotenv": "^16.4.5",
"eslint": "^9.9.0",
"graphql": "^16.9.0",
"graphql-subscriptions": "^2.0.0",
"inversify": "^6.0.2",
"ioredis": "^5.4.1",
"openai": "^4.56.0",
Expand Down
8 changes: 4 additions & 4 deletions server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ model Chat {
messages Message[]
actionNodeId String? @db.Uuid
actionNode ActionNode? @relation(fields: [actionNodeId], references: [id])
actionNodeId String @db.Uuid
actionNode ActionNode @relation(fields: [actionNodeId], references: [id])
Swarm Swarm? @relation(fields: [swarmId], references: [id])
swarmId String? @db.Uuid
swarm Swarm @relation(fields: [swarmId], references: [id])
swarmId String @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand Down
Loading

0 comments on commit 9f4cf9d

Please sign in to comment.