# Project export: ArguMentor

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2024
- Tagline: Hello! We are ArguMentor, and our application productively challenges your arguments rather than agreeing with them.
- Devpost: https://devpost.com/software/argumentor-261ukr
- GitHub: https://github.com/Bri-Guy/arguMentor
- Video: https://www.youtube.com/embed/4wG5hATxdMk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — amxq24 (20 commits), Aditi T (16 commits), Bri-Guy (15 commits)

## Devpost submission (written by the team)

### Inspiration

We believe that most LLMs are too agreeable, which is nice but not incredibly conducive to all use cases. That's where ArguMentor steps in - our application actively debates with you over your ideas to highlight flaws and weaknesses in your argument.

### What it does

Our application provides users with agents that can portray different scenarios, enabling users to find a customized scenario for their situation.

### How we built it

We fine-tuned our LLM to reflect more critical thinking with an Anthropic dataset using Together.ai. Additionally, we created a second model that was fine-tuned using Monster API. We also iterated on prompts to develop useful and generalizable AI agents for educational purposes, including simulated audience members for a panel discussion. Our front-end was modeled off of a Convex template and our backend is also powered by Convex.

### Challenges we ran into

This was our first hackathon, and we were relatively new to full-stack development, so we're proud of the progress and product we created. Some challenges we faced included: model fine-tuning and dataset formatting, multi-agent LLM tuning and prompt engineering, and integrating the front end with LLM API calls.

### Accomplishments we're proud of

We are proud by the emergent multi-agent conversations our system is capable of. Particularly, different agents are capable of responding to one another as if they were scholars debating a research topic. Additionally, we are proud of using LLM's in an unconventional light, guiding them to be less agreeable and more honest in helping users strengthen their logic and prepare for challenging situations.

### What we learned

We learned a great deal: front-end development, fine-tuning and prompt-engineering for LLM's, multi-agent LLM interactions, and full-stack development.

### What's next

We will continue iterating upon our models for better robustness and generalizability. We have many exciting ideas in education and sustainability applications related to this project!

## README (from the GitHub repository)

# ArguMentor

Hello! We are **ArguMentor**, and our application *productively challenges your arguments rather than agreeing with them*. 

We believe that most LLMs are too agreeable, which is nice but not incredibly conducive to all use cases. That's where ArguMentor steps in - our application actively debates with you over your ideas to highlight flaws and weaknesses in your argument. 

We see our application functioning primarily in the education space - whether being used to prepare for thesis defenses, research panels, or class discussions, our application provides users with questions that challenge ideas and provoke more critical thought.

![Screenshot 2024-02-17 at 11 53 06 PM](https://github.com/Bri-Guy/criticaLLM/assets/77946607/6e6970a5-1b83-434e-ae43-ed5eecf6a102)


# Educational Use Cases 

Our application provides users with agents that can portray different scenarios, enabling users to find a customized scenario for their situation. 

- A researcher preparing for a research panel might use our application to simulate a Q&A session to prepare for questions that might be asked
- A PhD student might use our application to get ready to defend their thesis
- Even in everyday scenarios, our application can be useful for simulating discussions involving multiple viewpoints to providing counterarguments for an essay.

If you're looking for something even more custom, you have the ability to even create custom agents yourself!

# How to Use

First, choose an agent that you want to simulate a situation with. After, input information about your situation - i.e. if the situation is a research fair, a class discussion, etc. Then, sit back and let our platform work its magic!  

![Screenshot 2024-02-17 at 11 55 09 PM](https://github.com/Bri-Guy/criticaLLM/assets/77946607/a4bd9ad1-1114-43c4-a4dc-360f90b3d6d2)


# Resources Used

We fine-tuned our LLM to reflect more critical thinking with an Anthropic dataset using [Together.ai](https://www.together.ai). Additionally, we created a second model that was fine-tuned using [Monster API](https://monsterapi.ai/). Our front-end was modeled off of a Convex template and our backend is also powered by [Convex](https://www.convex.dev). 






## Detected evidence (automated analysis)

Indexed codebase: 21 recognized source files, 47 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (31 of 31)

```
.gitignore
convex/_generated/api.d.ts
convex/_generated/api.js
convex/_generated/dataModel.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/identity.ts
convex/messages.ts
convex/monster.ts
convex/openai.ts
convex/README.md
convex/schema.ts
convex/threads.ts
convex/tsconfig.json
fonts/Biko_Black.otf
fonts/Biko_Bold.otf
fonts/Biko_Light Readme.rtf
fonts/Biko_Light-Restricted.otf
fonts/Biko_Regular.otf
index.html
package.json
README.md
src/App.tsx
src/components/AddIdentity.tsx
src/components/Thread.tsx
src/index.css
src/main.tsx
src/vite-env.d.ts
tsconfig.json
tsconfig.node.json
vite.config.ts
```

### Dependencies

- package.json: @types/node@^20.4.4, @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, convex@^1.0.2, eslint@^8.45.0, eslint-plugin-react-hooks@^4.6.0, eslint-plugin-react-refresh@^0.4.3, npm-run-all@^4.1.5, openai@^3.3.0, react@^18.2.0, react-dom@^18.2.0, typescript@^5.0.2, vite@^4.4.5

### Recent commits (newest first)

- safety
- Merge pull request #5 from Bri-Guy/ui-tests
- added monster file
- multi agent
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "convex-chat-gpt",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "npm-run-all dev:init --parallel dev:backend dev:frontend",
    "dev:init": "convex dev --until-success",
    "dev:backend": "convex dev",
    "dev:frontend": "vite --open --clearScreen false",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "convex": "^1.0.2",
    "openai": "^3.3.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/node": "^20.4.4",
    "@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",
    "npm-run-all": "^4.1.5",
    "typescript": "^5.0.2",
    "vite": "^4.4.5"
  }
}

```

### src/main.tsx

```typescript
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { ConvexProvider, ConvexReactClient } from "convex/react";

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <ConvexProvider client={convex}>
      <App />
    </ConvexProvider>
  </React.StrictMode>
);

```

### src/App.tsx

```typescript
import { api } from "../convex/_generated/api";
import { useMutation, usePaginatedQuery } from "convex/react";
import { useEffect, useState, useMemo } from "react";
import { AddIdentity } from "./components/AddIdentity";
import { Thread, UIMessage } from "./components/Thread";
import { Id } from "../convex/_generated/dataModel";

export default function App() {
  const { loadMore, results, status } = usePaginatedQuery(
    api.messages.list,
    {},
    {
      initialNumItems: 100,
    }
  );
  const messages = useMemo(() => results.slice().reverse(), [results]);

  const [newThreadId, setNewThreadId] = useState<Id<"threads">>();
  const createThread = useMutation(api.threads.add);
  useEffect(() => {
    if (newThreadId && messages.find((m) => newThreadId === m.threadId))
      setNewThreadId(undefined);
  }, [newThreadId, messages]);

  return (
    <main>
      <h1><img src="images/argumentor.png" alt="ArguMentor" /></h1>
      <p> <em>ArguMentor provides finetuned LLMs that can argue with you and provide useful criticism for your ideas!</em> </p>
      {status === "CanLoadMore" && (
        <button onClick={() => loadMore(100)}>Load More</button>
      )}
      <br></br>
      {messages
        .reduce<UIMessage[][]>((threads, message) => {
          const thread = threads.find(
            (threadMessages) => threadMessages[0].threadId === message.threadId
          );
          if (thread) {
            thread.push(message);
          } else {
            threads.push([message]);
          }
          return threads;
        }, [])
        .map((messages, index, threads) => (
          <details
            key={"thread" + index}
            open={!newThreadId && index === threads.length - 1}
          >
            <summary>{messages[0]?.body?.substring(0, 100)}...</summary>
            <Thread messages={messages} threadId={messages[0].threadId} />
          </details>
        ))}
      <br></br>
      {newThreadId && (
        <>
          <Thread messages={[]} threadId={newThreadId} />
        </>
      )}
      <button
        onClick={(e) => {
          e.preventDefault();
          createThread().then(setNewThreadId);
        }}
        disabled={!!newThreadId}
      >
        Start New Thread
      </button>
      <AddIdentity />
    </main>
  );
}

```

### convex/_generated/server.js

```javascript
/* eslint-disable */
/**
 * Generated utilities for implementing server-side Convex query and mutation functions.
 *
 * THIS CODE IS AUTOMATICALLY GENERATED.
 *
 * Generated by convex@1.9.0.
 * To regenerate, run `npx convex dev`.
 * @module
 */

import {
  actionGeneric,
  httpActionGeneric,
  queryGeneric,
  mutationGeneric,
  internalActionGeneric,
  internalMutationGeneric,
  internalQueryGeneric,
} from "convex/server";

/**
 * Define a query in this Convex app's public API.
 *
 * This function will be allowed to read your Convex database and will be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const query = queryGeneric;

/**
 * Define a query that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to read from your Convex database. It will not be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const internalQuery = internalQueryGeneric;

/**
 * Define a mutation in this Convex app's public API.
 *
 * This function will be allowed to modify your Convex database and will be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const mutation = mutationGeneric;

/**
 * Define a mutation that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to modify your Convex database. It will not be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const internalMutation = internalMutationGeneric;

/**
 * Define an action in this Convex app's public API.
 *
 * An action is a function which can execute any JavaScript code, including non-deterministic
 * code and code with side-effects, like calling third-party services.
 * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
 * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
 *
 * @param func - The action. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
 */
export const action = actionGeneric;

/**
 * Define an action that is only accessible from other Convex functions (but not from the client).
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
 */
export const internalAction = internalActionGeneric;

/**
 * Define a Convex HTTP action.
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
 * as its second.
 * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
 */
export const httpAction = httpActionGeneric;

```

### vite.config.ts

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

```

### index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="icon" href="/images/argumentor_A8o_icon.ico" type="image/x-icon">
    <title>ArguMentor</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### src/vite-env.d.ts

```typescript
/// <reference types="vite/client" />

```

### convex/threads.ts

```typescript
import { query, mutation } from "./_generated/server";

export const latest = query({
  handler: async (ctx) => {
    return ctx.db.query("threads").order("desc").first();
  },
});

export const add = mutation({
  args: {},
  handler: async (ctx, thread) => {
    return ctx.db.insert("threads", thread || {});
  },
});

```

### convex/schema.ts

```typescript
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  identities: defineTable({
    flagged: v.optional(v.string()),
    instructions: v.string(),
    name: v.string(),
  }),
  messages: defineTable({
    roleName: v.string(),
    author: v.union(v.literal("user"), v.literal("assistant")),
    threadId: v.id("threads"),
    // body starts out undefined for assistant messages
    body: v.optional(v.string()),
    // For messages authenticated with Clerk
    user: v.optional(v.any()),
    // For ChatGPT Messages
    error: v.optional(v.string()),
    identityId: v.optional(v.id("identities")),
    ms: v.optional(v.number()),
    updatedAt: v.optional(v.number()),
    usage: v.optional(
      v.object({
        completion_tokens: v.number(),
        prompt_tokens: v.number(),
        total_tokens: v.number(),
      })
    ),
  }),
  threads: defineTable({}),
});

```

### convex/identity.ts

```typescript
import { v } from "convex/values";
import { query, internalMutation } from "./_generated/server";

export const list = query({
  handler: async (ctx) => {
    const identities = await ctx.db
      .query("identities")
      .filter((q) => q.eq(q.field("flagged"), undefined))
      .collect();
    return identities.map((identity) => identity.name);
  },
});

export const add = internalMutation({
  args: { name: v.string(), instructions: v.string() },
  handler: async (ctx, { name, instructions }) => {
    instructions = "You are a helpful AI agent who is answering the user's questions. You will debate with the user and think critically." + instructions + ". NEVER SAY YOU ARE AN AI LANGUAGE MODEL.";
    const existing = await ctx.db
      .query("identities")
      .filter((q) => q.eq(q.field("name"), name))
      .unique();
    let identityId;
    if (existing) {
      identityId = existing._id;
      await ctx.db.patch(existing._id, { instructions });
    } else {
      identityId = await ctx.db.insert("identities", { name, instructions });
    }
    return identityId;
  },
});

```

[10 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]