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
9 changes: 0 additions & 9 deletions .env.example

This file was deleted.

3 changes: 3 additions & 0 deletions backend/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ func setupRouter(cfg *config.Config) *gin.Engine {
// Set up transcript routes
routes.SetupTranscriptRoutes(auth)

// Set up assumption extraction routes
routes.SetupAssumptionRoutes(auth)

auth.GET("/coach/strengthen-argument/weak-statement", routes.GetWeakStatement)
auth.POST("/coach/strengthen-argument/evaluate", routes.EvaluateStrengthenedArgument)

Expand Down
43 changes: 0 additions & 43 deletions backend/config/config.prod.sample.yml

This file was deleted.

49 changes: 49 additions & 0 deletions backend/controllers/assumption_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package controllers

import (
"net/http"

"arguehub/services"

"github.com/gin-gonic/gin"
)

// GetDebateAssumptions retrieves or generates assumptions for a debate
func GetDebateAssumptions(c *gin.Context) {
debateID := c.Param("debateId")

if debateID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "debateId is required",
})
return
}

// Extract assumptions (will use cache if available)
assumptions, err := services.ExtractAssumptions(debateID)
if err != nil {
// Determine appropriate error code
statusCode := http.StatusInternalServerError
errorMessage := err.Error()

// Provide user-friendly messages for common errors
if err.Error() == "AI service not available - Gemini client not initialized" {
statusCode = http.StatusServiceUnavailable
errorMessage = "AI analysis service is currently unavailable. Please ensure the Gemini API is configured."
} else if err.Error() == "no transcript found for this debate" {
statusCode = http.StatusNotFound
errorMessage = "No debate transcript found for this debate ID."
}

c.JSON(statusCode, gin.H{
"error": errorMessage,
})
return
}

// Return assumptions (even if empty)
c.JSON(http.StatusOK, gin.H{
"assumptions": assumptions,
"count": len(assumptions),
})
}
9 changes: 6 additions & 3 deletions backend/controllers/debatevsbot_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ type PhaseTiming struct {
}

type JudgeRequest struct {
History []models.Message `json:"history" binding:"required"`
DebateId string `json:"debateId" binding:"required"`
History []models.Message `json:"history" binding:"required"`
}
Comment on lines 36 to 39
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n backend/controllers/debatevsbot_controller.go | sed -n '30,50p'

Repository: AOSSIE-Org/DebateAI

Length of output: 182


🏁 Script executed:

# Check for JudgeRequest struct and related code
rg -n "JudgeRequest" --type go -A 5 -B 2

Repository: AOSSIE-Org/DebateAI

Length of output: 1206


🏁 Script executed:

# Look for frontend code making judge requests
find . -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" | head -20

Repository: AOSSIE-Org/DebateAI

Length of output: 782


🏁 Script executed:

# Get the full JudgeDebate function implementation
cat -n backend/controllers/debatevsbot_controller.go | sed -n '200,240p'

Repository: AOSSIE-Org/DebateAI

Length of output: 1745


🏁 Script executed:

# Search for judge endpoint calls in frontend
rg -n "judge" frontend/src/Pages/DebateRoom.tsx -i -A 5 -B 2

Repository: AOSSIE-Org/DebateAI

Length of output: 1388


🏁 Script executed:

# Look for any API endpoint definitions or constants
rg -n "/judge" --type go -B 5 -A 5

Repository: AOSSIE-Org/DebateAI

Length of output: 660


🏁 Script executed:

# Find the frontend judgeDebate service implementation
rg -n "export.*judgeDebate\|function.*judgeDebate\|const.*judgeDebate" frontend/ --type ts -A 10

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check for vsbot service file
find frontend -name "*vsbot*" -type f

Repository: AOSSIE-Org/DebateAI

Length of output: 93


🏁 Script executed:

# Look at the actual API call to see what's being sent
rg -n "debateId\|userId" frontend/src/Pages/DebateRoom.tsx -B 3 -A 3 | head -50

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

cat -n frontend/src/services/vsbot.ts

Repository: AOSSIE-Org/DebateAI

Length of output: 4743


🏁 Script executed:

# Also check the debateData structure to see if debateId is available
rg -n "debateData\|DebateData" frontend/src/Pages/DebateRoom.tsx -A 2 -B 2 | head -40

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check if debateData or similar structure stores debateId
rg -n "const.*debateData\|debateData.*=\|debateId" frontend/src/Pages/DebateRoom.tsx -B 2 -A 2 | head -60

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check what DebateResponse contains and how it's used
rg -n "createDebate\|DebateResponse" frontend/src/Pages/DebateRoom.tsx -B 2 -A 5

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Get full DebateRoom component to understand state structure
head -200 frontend/src/Pages/DebateRoom.tsx | cat -n

Repository: AOSSIE-Org/DebateAI

Length of output: 6657


🏁 Script executed:

# Check BotSelection for createDebate usage
rg -n "createDebate\|debateId" frontend/src/Pages/BotSelection.tsx -B 3 -A 3 | head -60

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Check if BotSelection is navigating to DebateRoom and passing debateId
rg -n "navigate\|DebateRoom\|debateId" frontend/src/Pages/BotSelection.tsx -B 2 -A 2 | head -80

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Look at line 580-583 in context to see the judgeDebateResult function more completely
sed -n '577,600p' frontend/src/Pages/DebateRoom.tsx | cat -n

Repository: AOSSIE-Org/DebateAI

Length of output: 1205


🏁 Script executed:

# Find debateData definition in DebateRoom
rg -n "debateData\|useState" frontend/src/Pages/DebateRoom.tsx | head -20

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Look for useLocation to see if debateData is passed through route state
sed -n '1,250p' frontend/src/Pages/DebateRoom.tsx | rg -n "useLocation\|debateData" -A 3

Repository: AOSSIE-Org/DebateAI

Length of output: 45


🏁 Script executed:

# Get the main component function signature and props handling
sed -n '200,350p' frontend/src/Pages/DebateRoom.tsx | cat -n

Repository: AOSSIE-Org/DebateAI

Length of output: 6037


🏁 Script executed:

# Search more broadly for how debateData is defined
rg -n "const.*=.*use\|debateData" frontend/src/Pages/DebateRoom.tsx | head -30

Repository: AOSSIE-Org/DebateAI

Length of output: 45


Frontend must be updated to pass debateId instead of userId to the /judge endpoint.

The backend now requires debateId in the JudgeRequest struct (binding:"required"). The frontend has debateId available from debateData but currently sends userId instead, which will cause all judge requests to fail with 400 Bad Request errors.

Updates needed:

  1. Modify JudgeRequest type in frontend/src/services/vsbot.ts (lines 35-38) to include debateId: string instead of userId: string
  2. Update judgeDebateResult in frontend/src/Pages/DebateRoom.tsx (line 580-583) to pass debateId: debateData.debateId instead of userId: debateData.userId

This aligns with the existing pattern in concedeDebate, which already passes debateId to the backend.

🤖 Prompt for AI Agents
In `@backend/controllers/debatevsbot_controller.go` around lines 36 - 39, The
frontend is still sending userId but the backend JudgeRequest requires debateId;
update the JudgeRequest type in the frontend service (rename the field userId:
string to debateId: string in the JudgeRequest/interface used in vsbot.ts) and
modify the caller judgeDebateResult to pass debateId: debateData.debateId
instead of userId: debateData.userId so judgeDebateResult sends debateId
matching the backend binding.


type DebateResponse struct {
Expand Down Expand Up @@ -207,8 +208,10 @@ func JudgeDebate(c *gin.Context) {
// Judge the debate
result := services.JudgeDebate(req.History)

// Update debate outcome
if err := db.UpdateDebateVsBotOutcome(email, result); err != nil {
// Update debate outcome using the provided debateId
if err := db.UpdateDebateVsBotOutcome(req.DebateId, result); err != nil {
log.Printf("Warning: Failed to update debate outcome for %s: %v", req.DebateId, err)
// We don't return here because the judging itself succeeded
}

// Get the latest debate information to extract proper details
Expand Down
24 changes: 18 additions & 6 deletions backend/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/redis/go-redis/v9"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
Expand Down Expand Up @@ -69,14 +70,25 @@ func SaveDebateVsBot(debate models.DebateVsBot) error {
return nil
}

// UpdateDebateVsBotOutcome updates the outcome of the most recent bot debate for a user
func UpdateDebateVsBotOutcome(userId, outcome string) error {
filter := bson.M{"userId": userId}
// UpdateDebateVsBotOutcome updates the outcome of a specific bot debate by its ID
func UpdateDebateVsBotOutcome(debateId, outcome string) error {
objID, err := primitive.ObjectIDFromHex(debateId)
if err != nil {
return fmt.Errorf("invalid debate ID: %w", err)
}

filter := bson.M{"_id": objID}
update := bson.M{"$set": bson.M{"outcome": outcome}}
_, err := DebateVsBotCollection.UpdateOne(context.Background(), filter, update, nil)

result, err := DebateVsBotCollection.UpdateOne(context.Background(), filter, update)
if err != nil {
return err
return fmt.Errorf("failed to update debate outcome: %w", err)
}

if result.MatchedCount == 0 {
return fmt.Errorf("no debate found with ID: %s", debateId)
}

return nil
}

Expand Down Expand Up @@ -115,4 +127,4 @@ func ConnectRedis(addr, password string, db int) error {

log.Println("Connected to Redis")
return nil
}
}
Binary file modified backend/main
Binary file not shown.
33 changes: 33 additions & 0 deletions backend/models/assumption.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package models

import (
"encoding/json"
"time"

"go.mongodb.org/mongo-driver/bson/primitive"
)

// DebateAssumption represents implicit assumptions extracted from a debate
type DebateAssumption struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
DebateID string `bson:"debateId" json:"debateId"`
ParticipantID string `bson:"participantId,omitempty" json:"participantId,omitempty"`
ParticipantEmail string `bson:"participantEmail,omitempty" json:"participantEmail,omitempty"`
Side string `bson:"side" json:"side"` // "for" or "against"
Assumptions []string `bson:"assumptions" json:"assumptions"`
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
}

// MarshalJSON customizes the JSON serialization to convert ObjectID to string
func (d DebateAssumption) MarshalJSON() ([]byte, error) {
type Alias DebateAssumption
a := Alias(d)
a.ID = primitive.NilObjectID
return json.Marshal(&struct {
ID string `json:"id"`
Alias
}{
ID: d.ID.Hex(),
Alias: a,
})
}
13 changes: 13 additions & 0 deletions backend/routes/assumption_routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package routes

import (
"arguehub/controllers"

"github.com/gin-gonic/gin"
)

// SetupAssumptionRoutes sets up routes for debate assumption extraction
func SetupAssumptionRoutes(router *gin.RouterGroup) {
// Get assumptions for a specific debate
router.GET("/debates/:debateId/assumptions", controllers.GetDebateAssumptions)
}
Loading