-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 08b3de1
Showing
36 changed files
with
2,400 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
### SERVER URL | ||
SERVER_URL="http://...:8001" | ||
SERVER_API_KEY="..." | ||
|
||
### SPEECH TO TEXT | ||
GROQ_API_KEY="gsk_..." | ||
GROQ_API_MODEL="whisper-large-v3" | ||
|
||
### LARGE LANGUAGE MODELS | ||
API_URL="https://.../v1/chat/completions" | ||
API_KEY="api-key" | ||
API_MODEL="gpt-4o" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
name: Deploy | ||
on: | ||
push: | ||
branches: | ||
- 'main' | ||
- 'preview' | ||
pull_request: | ||
branches: | ||
- 'main' | ||
- 'preview' | ||
|
||
jobs: | ||
deploy: | ||
name: Deploy | ||
runs-on: ubuntu-latest | ||
|
||
permissions: | ||
id-token: write # Needed for auth with Deno Deploy | ||
contents: read # Needed to clone the repository | ||
|
||
steps: | ||
- name: Clone repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Install Deno | ||
uses: denoland/setup-deno@v1 | ||
with: | ||
deno-version: v1.x | ||
|
||
- name: Build step | ||
run: "deno task build" # 📝 Update the build command(s) if necessary | ||
|
||
- name: Upload to Deno Deploy | ||
uses: denoland/deployctl@v1 | ||
with: | ||
project: "school-bud-e" # 📝 Update the deploy project name if necessary | ||
entrypoint: "./main.ts" # 📝 Update the entrypoint if necessary |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# dotenv environment variable files | ||
.env | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
.env.local | ||
.DS_Store | ||
.vscode | ||
|
||
# Fresh build directory | ||
_fresh/ | ||
# npm dependencies | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright (c) 2024 LAION e.V. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | ||
OR OTHER DEALINGS IN THE SOFTWARE./ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# School Bud-E 🎓🤖 | ||
|
||
![School Bud-E Banner](banner.png) | ||
|
||
Welcome to School Bud-E, your AI-powered educational assistant! 🚀 | ||
|
||
[![Join us on Discord](https://img.shields.io/discord/823813159592001537?color=5865F2&logo=discord&logoColor=white)](https://discord.gg/xBPBXfcFHd) | ||
|
||
## 🌟 Overview | ||
|
||
School Bud-E is an intelligent and empathetic learning assistant designed to revolutionize the educational experience. Developed by [LAION](https://laion.ai) in collaboration with the ELLIS Institute Tübingen, Collabora, the Tübingen AI Center and the DFKI, School Bud-E focuses on empathy, natural interaction, and personalized learning. | ||
|
||
## 🚀 Features (WIP) | ||
|
||
- 💬 Real-time responses to student queries | ||
- 🧠 Emotionally intelligent interactions | ||
- 🔄 Continuous conversation context | ||
- 👥 Multi-speaker and multi-language support | ||
- 🖥️ Local operation on consumer-grade hardware | ||
- 🔒 Privacy-focused design | ||
|
||
## 🛠️ Technology Stack | ||
|
||
- **Frontend**: Fresh framework (Preact-based) | ||
- **Styling**: Tailwind CSS | ||
- **Language Support**: Internationalization for English and German | ||
- **AI Models**: | ||
- Speech-to-Text: Whisper Large V3 (via Groq API) | ||
- Large Language Model: GPT-4o or equivalent | ||
|
||
## 🏗️ Project Structure | ||
|
||
- `routes/`: Application routes | ||
- `components/`: Reusable UI components | ||
- `islands/`: Interactive components (Fresh islands) | ||
- `internalization/`: Language-specific content | ||
- `static/`: Static assets | ||
|
||
## 🚀 Getting Started | ||
|
||
1. Clone the repository: | ||
|
||
```bash | ||
git clone https://github.com/LAION-AI/school-bud-e.git | ||
``` | ||
|
||
2. Install dependencies: | ||
|
||
```bash | ||
deno task start | ||
``` | ||
|
||
3. Set up environment variables: | ||
- Copy `.example.env` to `.env` | ||
- Fill in the required API keys and endpoints | ||
|
||
4. Run the development server: | ||
|
||
```bash | ||
deno task start | ||
``` | ||
|
||
5. Open `http://localhost:8000` in your browser | ||
|
||
## 🤝 Contributing | ||
|
||
We welcome contributions to School Bud-E! Please join our [Discord server](https://discord.com/invite/eq3cAMZtCC) or contact us at <contact@laion.ai> to get involved. | ||
|
||
## 🚧 Experimental Demo Version | ||
|
||
Please note that this is an early prototype application that may provide inaccurate answers or generate content that is not suitable for all audiences. We advise caution and encourage you to report any issues you encounter to us. | ||
|
||
## 📄 License | ||
|
||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. | ||
|
||
## 🙏 Acknowledgements | ||
|
||
Special thanks to LAION, ELLIS Institute Tübingen, Collabora, the Tübingen AI Center and the German Research Center for Artificial Intelligence (DFKI) for their contributions and support to this project. | ||
|
||
--- | ||
|
||
Built with ❤️ for the future of education. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Button.tsx | ||
import { JSX } from "preact"; | ||
import { IS_BROWSER } from "$fresh/runtime.ts"; | ||
|
||
export function ChatSubmitButton(props: JSX.HTMLAttributes<HTMLButtonElement>) { | ||
// Destructure `class` from props to apply alongside Tailwind classes | ||
const { class: className, ...buttonProps } = props; | ||
|
||
return ( | ||
<button | ||
{...buttonProps} | ||
// Spread the rest of the buttonProps here | ||
disabled={!IS_BROWSER || props.disabled} | ||
class={`absolute right-3 bottom-3 disabled:opacity-50 disabled:cursor-not-allowed rounded-md p-2 ${ | ||
props.disabled ? "bg-gray-100" : "bg-gray-400" | ||
} ${className}`} // Apply external class here | ||
> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="24" | ||
height="24" | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
stroke="currentColor" | ||
stroke-width="2" | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
class={`icon icon-tabler icons-tabler-outline icon-tabler-arrow-narrow-up ${ | ||
props.disabled ? "text-gray-400" : "text-white" | ||
}`} | ||
> | ||
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||
<path d="M12 5v14" /> | ||
<path d="M16 9l-4 -4l-4 4" /> | ||
</svg> | ||
</button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import { useEffect, useState } from "preact/hooks"; | ||
|
||
import { chatTemplateContent } from "../internalization/content.ts"; | ||
|
||
function ChatTemplate( | ||
{ | ||
lang, | ||
parentImages, | ||
messages, | ||
readAlways, | ||
audioFileDict, | ||
onRefreshAction, | ||
onSpeakAtGroupIndexAction, | ||
onImageChange, | ||
onToggleReadAlwaysAction, | ||
}: { | ||
lang: string; | ||
parentImages: Image[]; | ||
messages: Message[]; | ||
isComplete: boolean; | ||
readAlways: boolean; | ||
audioFileDict: AudioFileDict; | ||
onToggleReadAlwaysAction: () => void; | ||
onSpeakAtGroupIndexAction: (groupIndex: number) => void; | ||
onRefreshAction: (groupIndex: number) => void; | ||
onEditAction: (groupIndex: number) => void; | ||
onUploadActionToMessages: (uploadedMessages: Message[]) => void; | ||
onImageChange: (images: Image[]) => void; | ||
onTrashAction: () => void; | ||
}, | ||
) { | ||
const [images, setImages] = useState<Image[]>([]); | ||
const [imageFiles, setImageFiles] = useState<Image[]>([]); | ||
|
||
// deno-lint-ignore no-explicit-any | ||
const deleteImage = (event: any) => { | ||
const index = images.findIndex((image) => | ||
image.preview === event.target.src | ||
); | ||
const newImages = [...images]; | ||
const newImageFiles = [...imageFiles]; | ||
newImages.splice(index, 1); | ||
newImageFiles.splice(index, 1); | ||
setImages(newImages); | ||
setImageFiles(newImageFiles); | ||
onImageChange(newImageFiles); | ||
}; | ||
|
||
useEffect(() => { | ||
setImages(parentImages); | ||
}, [parentImages]); | ||
|
||
return ( | ||
<div | ||
class={messages?.length === 0 | ||
? `bg-transparent` | ||
: `chat-history flex flex-col space-y-4 p-4 mx-auto rounded-lg shadow bg-white/75`} | ||
> | ||
<button | ||
class={`absolute top-0 left-0 m-4 text-xs align-middle text-gray-600 hover:text-gray-800 transition-colors`} | ||
onClick={() => onToggleReadAlwaysAction()} | ||
> | ||
{readAlways ? chatTemplateContent[lang]["readOutText"] : chatTemplateContent[lang]["silent"]} | ||
</button> | ||
{messages?.map((item, groupIndex) => { | ||
return ( | ||
<div | ||
key={groupIndex} | ||
class={`message-group flex flex-col ${ | ||
item.role === "user" ? "items-end" : "items-start" | ||
}`} | ||
> | ||
<span | ||
class={`text-sm font-semibold flex justify-center items-center ${ | ||
item.role === "user" ? "text-blue-600" : "text-gray-600" | ||
}`} | ||
> | ||
{item.role === "user" ? "Du" : "School Bud-E"} | ||
{item.role !== "user" && groupIndex !== 0 && ( | ||
<button onClick={() => onRefreshAction(groupIndex)}> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
style="margin-left: 0.5rem; width: 24px; height: 24px;" | ||
viewBox="0 -960 960 960" | ||
fill="grey" | ||
> | ||
<path d="M440-122q-121-15-200.5-105.5T160-440q0-66 26-126.5T260-672l57 57q-38 34-57.5 79T240-440q0 88 56 155.5T440-202v80Zm80 0v-80q87-16 143.5-83T720-440q0-100-70-170t-170-70h-3l44 44-56 56-140-140 140-140 56 56-44 44h3q134 0 227 93t93 227q0 121-79.5 211.5T520-122Z" /> | ||
</svg> | ||
</button> | ||
)} | ||
{item.role !== "user" && ( | ||
<button onClick={() => onSpeakAtGroupIndexAction(groupIndex)}> | ||
{!audioFileDict[groupIndex] || | ||
!audioFileDict[groupIndex].some((audioFile) => !audioFile.paused) | ||
? ( | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
style="margin-left: 0.5rem; width: 24px; height: 24px;" | ||
viewBox="0 -960 960 960" | ||
fill="currentColor" | ||
> | ||
<path d="M560-131v-82q90-26 145-100t55-168q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 127-78 224.5T560-131ZM120-360v-240h160l200-200v640L280-360H120Zm440 40v-322q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560-320ZM400-606l-86 86H200v80h114l86 86v-252ZM300-480Z" /> | ||
</svg> | ||
) | ||
: ( | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
style="margin-left: 0.5rem; width: 24px; height: 24px;" | ||
viewBox="0 -960 960 960" | ||
fill="currentColor" | ||
> | ||
<path d="M320-320h320v-320H320v320ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" /> | ||
</svg> | ||
)} | ||
</button> | ||
)} | ||
</span> | ||
<div | ||
class={`message mt-1 whitespace-pre-wrap [overflow-wrap:anywhere] ${ | ||
item.role === "user" | ||
? "bg-blue-100 sm:ml-20 md:ml-40" | ||
: "bg-gray-100 sm:mr-20 md:mr-40" | ||
} p-3 rounded-lg ${ | ||
item.role === "user" ? "rounded-tr-none" : "rounded-tl-none" | ||
} shadow`} | ||
> | ||
{typeof item.content === "string" | ||
? <span>{item.content}</span> | ||
: ( | ||
<span> | ||
{typeof item.content[0] === "string" | ||
? item.content.join("") | ||
: ( | ||
<div> | ||
{(item.content as unknown as { | ||
"type": string; | ||
"text": string; | ||
"image_url": { url: string }; | ||
}[]).map((content, contentIndex) => { | ||
if (content.type === "text") { | ||
return ( | ||
<span key={contentIndex}>{content.text}</span> | ||
); | ||
} else if (content.type === "image_url") { | ||
return ( | ||
<img | ||
key={contentIndex} | ||
src={content.image_url.url} | ||
alt="User uploaded image" | ||
class="max-w-full h-auto rounded-lg shadow-sm" | ||
/> | ||
); | ||
} | ||
})} | ||
</div> | ||
)} | ||
</span> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
})} | ||
{images.length > 0 && ( | ||
<div class="w-full flex justify-center"> | ||
<div class="p-2 flex flex-wrap max-w-xs gap-8"> | ||
{images.map((image, index) => ( | ||
<img | ||
key={index} | ||
src={image.image_url.url} | ||
onClick={deleteImage} | ||
alt={`Thumbnail ${index + 1}`} | ||
class="w-32 h-32 object-cover rounded-lg shadow-xl bg-white/50 cursor-pointer hover:bg-red-500/50" | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export default ChatTemplate; |
Oops, something went wrong.