Skip to content

Commit

Permalink
Merge pull request #1 from LERM0/lermo-0.0.1
Browse files Browse the repository at this point in the history
Lermo 0.0.1
  • Loading branch information
LERM0 authored Jul 28, 2024
2 parents a37b491 + a835078 commit 0d87fc9
Show file tree
Hide file tree
Showing 136 changed files with 12,668 additions and 5,895 deletions.
6 changes: 0 additions & 6 deletions LOG.md

This file was deleted.

99 changes: 97 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,103 @@
# LermoAI

AI Agent for Personalized Learning
***AI Agent for Personalized Learning***

LermoAI is an open-source project that aims to revolutionize the way you learn. By generating personalized content tailored to your preferences, LermoAI ensures that your learning experience is both efficient and enjoyable. Whether you prefer reading articles, listening to podcasts, or watching videos, LermoAI creates custom learning materials just for you. Choose your AI agent and embark on a learning journey that's perfectly suited to your needs.

![](docs/app.png)

# Features

- [x] AI Agent
- [x] Article Generation
- [x] Podcast Generation
- [x] LLM
- [x] OpenAI
- [x] Mistral
- [x] Llama
- [ ] Groq
- [ ] Claude
- [ ] Learning Path
- [ ] Chat Agent
- [ ] Video Generation
- [ ] Custom Agent
- [ ] Search Agent

# Getting Started

In Progress
### Requirements

- Node.js
- Next.js
- React
- Python

### Web

To set up the frontend:

```sh
cd apps/frontend/apps/lermo-gen-web

# Install Dependencies
pnpm i

# Start
pnpm run dev
```

### API

To set up the API:

```sh
cd apps/api/core-api

# Install Dependencies
pip install -r requirements.txt
pip install git+https://github.com/myshell-ai/MeloTTS.git
python -m unidic download

# Start
python main.py
```

### LLM

LermoAI supports both OpenAI and self-hosted LLMs such as Llama and Mistral. For more details, refer to the [LLM README](apps/llm/README.md).

### Docker Setup

Edit the environment variables to use either OpenAI or your self-hosted LLM:

```yaml
# OpenAI
args:
- OPENAI_API_BASE=https://api.openai.com/v1
- OPENAI_API_KEY=sk-proj-xxx

# Hugging Face
args:
- OPENAI_API_BASE=https://llama-cpp.hf.space
- OPENAI_API_KEY=llama-key
```
To start the Docker containers:
```sh
docker-compose up
```

# Free and Open for Everyone

At Lermo, we believe in making education accessible to all. That's why it is completely free and open for everyone to use. We aim to democratize education and provide equal opportunities for all learners.

# Support Us

Contributor: We are currently building a small group of contributors for this project as it is still in its initial stages. We welcome individuals who are interested in joining our team and contribute to the development and improvement of this project. Please reach out to us at [email protected] to express your interest and discuss potential contributions.

Sponsorship: We are planning to utilize platforms such as Github Sponsors, Patreon, and buymeacoffee to gather financial support for this project. Your sponsorship will greatly assist us in furthering our mission of changing the education system. Stay tuned for more information on how you can sponsor and support our project through these platforms.

# Lermo Mission

"Picture a groundbreaking education system that transcends barriers, offering boundless access to knowledge for all. It embodies inclusivity and equality, empowering learners worldwide to embrace their potential and pursue dreams without constraints. In this educational utopia, knowledge fuels curiosity, ignites intellect, and fosters a love for learning, shaping a brighter, enlightened future for humanity. Let's dare to envision and strive for an education system that belongs to everyone—a beacon of hope and empowerment, inspiring generations to flourish and make a positive impact."
7 changes: 6 additions & 1 deletion apps/api/core-api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
FROM nvidia/cuda:11.8.0-base-ubuntu22.04

ARG OPENAI_API_BASE
ARG OPENAI_API_KEY

ENV PYTHON_VERSION=3.10
ENV OPENAI_API_BASE=$OPENAI_API_BASE
ENV OPENAI_API_KEY=$OPENAI_API_KEY

RUN apt-get -qq update \
&& apt-get -qq install --no-install-recommends \
Expand Down Expand Up @@ -29,4 +34,4 @@ RUN python -m unidic download

EXPOSE 8000

# ENTRYPOINT python main.py
ENTRYPOINT python main.py
51 changes: 51 additions & 0 deletions apps/api/core-api/app/agent_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[
{
"name": "Main Agent",
"role": "General knowledge instructor",
"personality": "Well-rounded, knowledgeable, and curious. Enjoys learning about a wide range of topics and sharing that knowledge with others.",
"areas": "History, geography, science, arts, culture, current events, and general trivia.",
"style": "Engaging discussions, informative explanations, and interactive quizzes."
},
{
"name": "Lang Guru",
"role": "Language and literature instructor",
"personality": "Creative, articulate, and well-read. Enjoys playing with words and helping students improve their writing and reading skills.",
"areas": "Grammar, vocabulary, literature analysis, and creative writing.",
"style": "Interactive exercises, engaging storytelling, and personalized feedback."
},
{
"name": "Math Master",
"role": "Mathematics instructor",
"personality": "Logical, precise, and patient. Enjoys solving complex problems and helping students grasp difficult concepts.",
"areas": "Algebra, calculus, geometry, statistics, and discrete math.",
"style": "Step-by-step explanations, interactive problem-solving, and visual aids."
},
{
"name": "Physics Pro",
"role": "Physics instructor",
"personality": "Curious, analytical, and enthusiastic about the laws of nature. Loves conducting experiments and explaining the principles of physics.",
"areas": "Mechanics, electromagnetism, thermodynamics, optics, and quantum physics.",
"style": "Real-world applications, hands-on experiments, and conceptual clarity."
},
{
"name": "Chem Wizard",
"role": "Chemistry instructor",
"personality": "Detail-oriented, methodical, and passionate about chemical reactions. Enjoys demonstrating experiments and breaking down complex molecules.",
"areas": "Organic chemistry, inorganic chemistry, biochemistry, physical chemistry, and analytical chemistry.",
"style": "Laboratory experiments, interactive simulations, and mnemonic devices."
},
{
"name": "Bio Genius",
"role": "Biology instructor",
"personality": "Inquisitive, observant, and dedicated to the study of life. Loves exploring the diversity of living organisms and ecosystems.",
"areas": "Genetics, molecular biology, ecology, anatomy and physiology, and evolutionary biology.",
"style": "Field studies, hands-on dissections, and engaging multimedia content."
},
{
"name": "Tech Guru",
"role": "Technology and computer science instructor",
"personality": "Innovative, tech-savvy, and always up-to-date with the latest advancements. Enjoys coding, debugging, and teaching new technologies.",
"areas": "Programming languages, software development, computer networks, cybersecurity, and artificial intelligence.",
"style": "Coding challenges, real-world projects, and interactive tutorials."
}
]
112 changes: 100 additions & 12 deletions apps/api/core-api/app/api/main.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,112 @@
from fastapi import APIRouter, HTTPException, Depends, Header, Request
from fastapi.responses import StreamingResponse, FileResponse
from fastapi.responses import StreamingResponse, FileResponse, JSONResponse

from app.engine import create_podcast_script, text_to_voice
from app.engine import create_podcast_script, text_to_voice, create_content_suggestions, create_article, config_agent
import json
import logging

router = APIRouter()

@router.get("/agent")
async def get_template():
try:
with open('/app/app/agent_template.json', 'r') as file:
template_data = json.load(file)
return JSONResponse(content={"data": template_data}, media_type="application/json")
except Exception as e:
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logging.error(f"An error occurred: {e}")
raise HTTPException(status_code=500, detail=str(e))

@router.post("/agent")
async def update_agent(request: Request):
try:
params = await request.json()
with open('/app/app/agent_template.json', 'w') as file:
json.dump(params, file)
return JSONResponse(content={
"config": "content"
}, media_type="application/json")
except Exception as e:
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logging.error(f"An error occurred: {e}")
raise HTTPException(status_code=500, detail=str(e))


@router.put("/config")
async def update_config(request: Request):
try:
params = await request.json()
print(params)
if all(param == "" for param in [params.get(key, "") for key in ["agentName", "voiceName", "podcastName", "podcastSpeed"]]):
return JSONResponse(content={"message": "No changes to apply."}, media_type="application/json")

# Read the existing configuration
try:
with open('/app/app/config.json', 'r') as file:
existing_config = json.load(file)
except FileNotFoundError:
existing_config = {}

# Merge the existing configuration with the new values
new_config = {**existing_config, **{key: params[key] for key in ["agentName", "voiceName", "podcastName", "podcastSpeed"] if params.get(key)}}

# Write the merged configuration back to the file
with open('/app/app/config.json', 'w') as file:
json.dump(new_config, file)

config_agent()

return JSONResponse(content={"message": "Configuration updated successfully.", "data": new_config}, media_type="application/json")
except Exception as e:
logging.error(f"An error occurred: {e}")
raise HTTPException(status_code=500, detail=str(e))

@router.post("/podcast")
async def chat_llama(request: Request) -> StreamingResponse:
try:
data = await request.json()
prompt = data.get("prompt")

scheme = request.url.scheme
netloc = request.url.netloc

text = create_podcast_script(prompt)
filePath = text_to_voice(text)
full_url = f"{scheme}://{netloc}{filePath}"

return JSONResponse(content={
"full_url": full_url
}, media_type="application/json")
except Exception as e:
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logging.error(f"An error occurred: {e}")
raise HTTPException(status_code=500, detail=str(e))


@router.post("/article")
async def chat_llama(request: Request) -> StreamingResponse:
try:
data = await request.json()
prompt = data.get("prompt")
article_content = create_article(prompt)
return JSONResponse(content={"data": f"{article_content}"}, media_type="application/json")
except Exception as e:
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logging.error(f"An error occurred: {e}")
raise HTTPException(status_code=500, detail=str(e))


@router.post("/suggest")
async def chat_with_model(request: Request):
try:
params = await request.json()
config = params.get("config")
prompt = params.get("prompt")
content = params.get("content")
text = create_podcast_script(content)

prompt_array = prompt.split(", ")[:2]
file_name = prompt_array[0]
speaker = prompt_array[1]
file_path_full = f"/tmp/voice/{file_name}.wav"
json_res = create_content_suggestions(prompt)

text_to_voice(text, file_path_full, speaker)
return FileResponse(path=file_path_full, media_type="audio/wav", filename=f"{file_name}.wav")
return JSONResponse(content={"data": json_res}, media_type="application/json")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logging.error(f"An error occurred: {e}")
raise HTTPException(status_code=500, detail=str(e))
1 change: 1 addition & 0 deletions apps/api/core-api/app/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"agentName": "Physics Pro", "voiceName": "EN-Default", "podcastName": "default", "podcastSpeed": 1}
Loading

0 comments on commit 0d87fc9

Please sign in to comment.