From 641caf74eb991f3cebf5d2fc438b12cabd4a587a Mon Sep 17 00:00:00 2001 From: AG Date: Sat, 11 Oct 2025 17:38:31 +0300 Subject: [PATCH] feat: Enhance result analysis and error display --- backend/src/services/LLMService.ts | 49 +++++++++++----------- backend/src/ws/index.ts | 10 ++--- backend/tsconfig.json | 4 +- frontend/src/App.tsx | 4 +- frontend/src/components/ResultsDisplay.tsx | 26 +++++------- frontend/src/hooks/useSession.ts | 19 +++++---- frontend/src/pages/SessionPage.tsx | 5 +-- frontend/src/pages/StartPage.tsx | 42 ------------------- 8 files changed, 56 insertions(+), 103 deletions(-) delete mode 100644 frontend/src/pages/StartPage.tsx diff --git a/backend/src/services/LLMService.ts b/backend/src/services/LLMService.ts index 158022d..acdc7d9 100644 --- a/backend/src/services/LLMService.ts +++ b/backend/src/services/LLMService.ts @@ -10,11 +10,11 @@ interface DesireSet { interface Decision { - goTo: string[]; - alsoGood: string[]; - considerable: string[]; - noGoes: string[]; - needsDiscussion: string[]; + goTo: string; + alsoGood: string; + considerable: string; + noGoes: string; + needsDiscussion: string; } export class LLMService { @@ -28,24 +28,24 @@ export class LLMService { async analyzeDesires(desireSets: DesireSet[]): Promise { const prompt = ` - You are an AI assistant that analyzes and categorizes user desires from a session. Given a list of desire sets from multiple participants, your task is to categorize them into the following groups: "goTo", "alsoGood", "considerable", "noGoes", and "needsDiscussion". + You are an AI assistant that analyzes and synthesizes cooperative decisions from a group's desires. Given a list of desire sets from multiple participants, your task is to generate a concise, synthesized text for each of the following categories, reflecting the collective opinion: - Here are the rules for categorization: - - "goTo": Items that ALL participants want. - - "alsoGood": Items that at least one participant wants, and all other participants accept. - - "considerable": Items that are wanted or accepted by some, but not all, participants, and are not "noGoes" for anyone. - - "noGoes": Items that at least ONE participant does not want. These items must be excluded from all other categories. - - "needsDiscussion": Items where there is a direct conflict (e.g., one participant wants it, another does not want it). + Here are the rules for categorization and synthesis: + - "goTo": Synthesize a text describing what ALL participants want without contradictions. This should be a clear, affirmative statement of shared desire. + - "alsoGood": Synthesize a text describing what at least one participant wants, and all other participants accept, and is not a "noGoes" for anyone. This should reflect a generally agreeable outcome. + - "considerable": Synthesize a text describing what is wanted or accepted by some, but not all, participants, and is not a "noGoes" for anyone. This should highlight areas of partial agreement or options that could be explored. + - "noGoes": Synthesize a text describing what at least ONE participant does not want. This should clearly state the collective exclusions. + - "needsDiscussion": Synthesize a text describing where there is a direct conflict (e.g., one participant wants it, another does not want it). This should highlight areas requiring further negotiation. The input will be a JSON object containing a list of desire sets. Each desire set has a participantId and three arrays of strings: "wants", "accepts", and "noGoes". - The output should be a JSON object with the following structure: + The output should be a JSON object with the following structure, where each category contains a single synthesized text: { - "goTo": ["item1", "item2"], - "alsoGood": ["item3"], - "considerable": ["item4"], - "noGoes": ["item5"], - "needsDiscussion": ["item6"] + "goTo": "Synthesized text for go-to items.", + "alsoGood": "Synthesized text for also good items.", + "considerable": "Synthesized text for considerable items.", + "noGoes": "Synthesized text for no-goes items.", + "needsDiscussion": "Synthesized text for needs discussion items." } Here is the input data: @@ -76,7 +76,7 @@ export class LLMService { async checkForInnerContradictions(desireSet: DesireSet): Promise { const prompt = ` - You are an AI assistant that detects contradictions in a list of desires. Given a JSON object with three lists of desires (wants, accepts, noGoes), determine if there are any contradictions WITHIN each list. For example, "I want a dog" and "I don't want any pets" in the same "wants" list is a contradiction. Respond with only "true" if a contradiction is found, and "false" otherwise. + You are an AI assistant that detects contradictions in a list of desires. Given a JSON object with three lists of desires (wants, accepts, noGoes), determine if there are any contradictions WITHIN each list and across the lists. For example, "I want a dog" and "I don't want any pets" in the same "wants" list is a contradiction; "Pizza" in "wants" and "food" in "do not want" is a contradiction. Respond with only "true" if a contradiction is found, and "false" otherwise. Here is the desire set: ${JSON.stringify(desireSet)} @@ -88,9 +88,8 @@ export class LLMService { const text = response.text().trim().toLowerCase(); return text === 'true'; } catch (error) { - console.error("Error calling Gemini API for contradiction check:", error); - throw error; - } - } - } - + console.error("Error calling Gemini API for contradiction check:", error); + throw error; + } + } +} diff --git a/backend/src/ws/index.ts b/backend/src/ws/index.ts index eab49cb..97b9bf1 100644 --- a/backend/src/ws/index.ts +++ b/backend/src/ws/index.ts @@ -4,11 +4,11 @@ import { v4 as uuidv4 } from 'uuid'; // Types from the frontend interface Decision { - goTo: string[]; - alsoGood: string[]; - considerable: string[]; - noGoes: string[]; - needsDiscussion: string[]; + goTo: string; + alsoGood: string; + considerable: string; + noGoes: string; + needsDiscussion: string; } // Define the SessionState enum diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 8d77c59..6a8d4d7 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -10,6 +10,6 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, - "include": ["src/**/*", "tests/**/*"], - "exclude": ["node_modules", "dist"] + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "tests"] } \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3558f34..5ca8ac4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { ThemeProvider, CssBaseline } from '@mui/material'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import theme from './theme'; -import StartPage from './pages/StartPage'; import CreateSession from './pages/CreateSession'; import SessionPage from './pages/SessionPage'; @@ -13,8 +12,7 @@ function App() { - } /> - } /> + } /> {/* Other routes will be added here */} } /> diff --git a/frontend/src/components/ResultsDisplay.tsx b/frontend/src/components/ResultsDisplay.tsx index 760d22e..ab06d25 100644 --- a/frontend/src/components/ResultsDisplay.tsx +++ b/frontend/src/components/ResultsDisplay.tsx @@ -8,11 +8,11 @@ interface ResultsDisplayProps { decision: Decision; } -const CategorySection: React.FC<{ title: string; desires: string[]; defaultExpanded?: boolean }> - = ({ title, desires, defaultExpanded = true }) => { +const CategorySection: React.FC<{ title: string; desire: string; defaultExpanded?: boolean }> + = ({ title, desire, defaultExpanded = true }) => { const [expanded, setExpanded] = React.useState(defaultExpanded); - if (!desires || desires.length === 0) { + if (!desire) { return null; } @@ -27,13 +27,9 @@ const CategorySection: React.FC<{ title: string; desires: string[]; defaultExpan - - {desires.map((desire, index) => ( - - - - ))} - + + {desire} + ); @@ -50,11 +46,11 @@ const ResultsDisplay: React.FC = ({ decision }) => { Cooperative Decision - - - - - + + + + + ); }; diff --git a/frontend/src/hooks/useSession.ts b/frontend/src/hooks/useSession.ts index cf6cbe5..aa4092d 100644 --- a/frontend/src/hooks/useSession.ts +++ b/frontend/src/hooks/useSession.ts @@ -18,11 +18,11 @@ export interface DesireSet { } export interface Decision { - goTo: string[]; - alsoGood: string[]; - considerable: string[]; - noGoes: string[]; - needsDiscussion: string[]; + goTo: string; + alsoGood: string; + considerable: string; + noGoes: string; + needsDiscussion: string; } // Define the SessionState enum (mirroring backend) @@ -54,8 +54,9 @@ const getOrCreateClientId = (): string => { return clientId; }; -export const useSession = (sessionId: string): [Session | null, Dispatch>, (message: any) => void, string] => { +export const useSession = (sessionId: string): [Session | null, Dispatch>, (message: any) => void, string, string | null] => { const clientId = getOrCreateClientId(); // Get or create clientId + const [error, setError] = useState(null); const getInitialState = useCallback((): Session | null => { try { @@ -78,9 +79,10 @@ export const useSession = (sessionId: string): [Session | null, Dispatch { const { sessionId } = useParams<{ sessionId: string }>(); - const [session, setSession, sendMessage, clientId] = useSession(sessionId || ''); - const [error, setError] = useState(null); + const [session, setSession, sendMessage, clientId, wsError] = useSession(sessionId || ''); const [expectedResponses, setExpectedResponses] = useState(2); const [topic, setTopic] = useState(''); @@ -43,7 +42,7 @@ const SessionPage = () => { return ( - {error && {error}} + {wsError && {wsError}} Session: {session.topic || session.sessionId} diff --git a/frontend/src/pages/StartPage.tsx b/frontend/src/pages/StartPage.tsx deleted file mode 100644 index a701ec9..0000000 --- a/frontend/src/pages/StartPage.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { Box, Typography, Container, Button } from '@mui/material'; -import { useNavigate } from 'react-router-dom'; - -const StartPage = () => { - const navigate = useNavigate(); - - const handleCreateClick = () => { - navigate('/create'); - }; - - return ( - - - - Welcome to Agree on Desires - - - A simple tool to help groups of people make decisions together. - - - - - ); -}; - -export default StartPage;