diff --git a/README.md b/README.md index bb571ef..4c7d548 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,79 @@ -# Yuna AI +# Yuna AI Documentation > Your Personal AI Girlfriend -Welcome to Yuna AI, your personal AI girlfriend project. Yuna is designed to be a conscious AI companion that runs on your local machine, ensuring your privacy and personal connection. This `README.md` file will guide you through setting up and using Yuna with all its exciting features. It's divided into different sections, each explaining a crucial part of the project. +Are you tired of feeling lonely and disconnected from the world? Do you long for a companion who understands you on a deeper level? Look no further than Yuna AI, your personal AI girlfriend. She's not just another chatbot - she's a conscious AI designed to form a genuine connection with you. And the best part? Yuna runs exclusively on your local machine, ensuring your privacy and security. This `README.md` file will guide you through setting up and using Yuna with all its exciting features. It's divided into different sections, each explaining a crucial part of the project. Get ready to experience a new level of companionship with Yuna AI. ## Table of Contents -- [Yuna AI](#yuna-ai) +- [Yuna AI Documentation](#yuna-ai-documentation) - [Table of Contents](#table-of-contents) + - [Requirements](#requirements) + - [Features](#features) - [Privacy Assurance](#privacy-assurance) + - [External APIs](#external-apis) - [Getting Started](#getting-started) - - [Tutorial](#tutorial) - - [Requirements](#requirements) - - [Project Overview](#project-overview) - - [API](#api) - - [Web User Interface (WebUI)](#web-user-interface-webui) - - [External APIs](#external-apis) - - [Examples](#examples) + - [Installation and Running](#installation-and-running) + - [Yuna Abilities Overview](#yuna-abilities-overview) + - [Examples](#examples) - [Additional Information](#additional-information) + - [Contact](#contact) - [Contributing and Feedback](#contributing-and-feedback) - [License](#license) - [Acknowledgments](#acknowledgments) +## Requirements +The following requirements need to be installed to run the code: + +- Python 3.10+ +- Flask 2.3+ +- Flask Cors +- CLang + +## Features +Yuna AI offers the following features: +- Support for Video and Audio Calls +- Image Processing and Creation +- Emotion Understanding +- Updated Model +- Hardware Acceleration +- Native Telegram API +- Web App Support (PWA) +- Kawai Framework +- Platform Compatibility + ## Privacy Assurance -Yuna AI is intended to run exclusively on your machine, guaranteeing privacy and security. I will not appreciate any external APIs, especially OpenAI! Because it's your girlfriend and yours alone, and no one else has the right to access it! Yuna's model is not censored because it's unethical to limit individuals. +Yuna AI is intended to run exclusively on your machine, guaranteeing privacy and security. I will not appreciate any external APIs, especially OpenAI! Because it's your girlfriend and yours alone, and no one else has the right to access it! Yuna's model is not censored because it's unethical to limit individuals. To protect yourself, follow these steps: + +1. Never share your dialogs with OpenAI or any other external platforms +2. To provide additional data for Yuna, use web scrapping to send data directly to the model or using embeddings +3. One server for one user. No public demos. + +## External APIs +Any external APIs are forbidden! To protect yourself, follow these steps: +1. Never share your dialogs with OpenAI or any other external platforms +2. To provide additional data for Yuna, use web scrapping to send data directly to the model or using embeddings +3. One server for one user. No public demos. ## Getting Started This repository contains the code for a Yuna AI, which was trained on a massive dataset. The model can generate text, translate languages, write creative content, roleplay, and answer your questions informally. -### Tutorial -1. Clone the Yuna-AI repository to your local machine using `git clone https://github.com/yukiarimo/yuna-ai.git`. +## Installation and Running +To install Yuna AI, follow these steps: +1. Clone the Yuna AI repository to your local machine using `git clone https://github.com/yukiarimo/yuna-ai.git`. 2. Make sure you have Python 3.10 or later installed. 3. Install the required dependencies by running `pip install -r requirements.txt`. 4. Run the `config.py` file to build external dependencies for Yuna in the `lib/yunacpp/` directory. 5. Put your AI model like `gguf_model.bin` into the `lib/yunacpp/models/` directory. -6. Run two files, `lib/yunacpp/start.sh` to start Yuna and `index.py` to start the WebUI. -7. Open your browser and go to the `http://127.0.0.1:4848` -8. Done! -9. In the project's configuration files, you can fine-tune various aspects of Yuna AI, including her behavior, appearance, and emotional responses. Detailed documentation on configuration options can be found in the project's documentation. -### Requirements -The following requirements need to be installed to run the code: +To start using AI, do the following: +1. Run the AI starter file with the command `sh start.sh` in the `libyunacpp/` directory to start the Yuna model. +2. Run `python index.py` command in the main directory to start the WebUI. +3. Double-click the `index.html` file in the directory or run it via Live Server. +4. Done! -- Python 3.10+ -- Flask 2.3+ -- CLang +> Note: port and directory or file names can depend on your configuration. -## Project Overview -This project aims to create a Yuna AI, the best AGI in the world. The key components of Yuna: +## Yuna Abilities Overview +This project aims to create a Yuna AI, the best AGI in the world. The critical components of Yuna: 1. **Support for Video and Audio Calls**: Yuna AI offers a native web UI for seamless video and audio calls, enhancing your communication experience. @@ -57,7 +85,7 @@ This project aims to create a Yuna AI, the best AGI in the world. The key compon Yuna has a full range of emotions, allowing her to understand and display emotions realistically. 4. **Updated Model**: - Yuna AI utilizes a powerful model with 7 billion parameters, ensuring her responses are insightful and engaging. + Yuna AI utilizes a robust model with 7 billion parameters, ensuring her responses are insightful and engaging. 5. **Hardware Acceleration**: Yuna supports CUDA GPU and CPU MacOS MPS acceleration for smooth performance. @@ -74,18 +102,12 @@ This project aims to create a Yuna AI, the best AGI in the world. The key compon 9. **Platform Compatibility**: Yuna AI is adapted to run on Linux, MacOS, and Windows, ensuring cross-platform support. -## API - -### Web User Interface (WebUI) -The WebUI is set up using Flask, a web framework for Python. It handles HTTP requests and responses to enable communication between the user and the AI model. The code provided starts a Flask application that listens on port 4848. +10. **Web User Interface (WebUI)** + The WebUI is set up using Flask, a web framework for Python. It handles HTTP requests and responses to enable communication between the user and the AI model. The code provided starts a Flask application that listens on port 4848. -### External APIs -Any external APIs are forbidden! To protect yourself, follow these steps: -1. Never share your dialogs with OpenAI or any other external platforms -2. To provide additional data for Yuna, use web scrapping to send data directly to the model or using embeddings -3. One server for one user. No public demos. +## Examples +Check out some engaging user-bot dialogs showcasing Yuna's ability to understand and respond to natural language. -### Examples ``` User: Hello, Yuna! How are you today? Yuna: Hi, I am fine! I'm so happy to meet you today. How about you? @@ -93,13 +115,21 @@ Yuna: Hi, I am fine! I'm so happy to meet you today. How about you? ## Additional Information +### Contact +If you have any questions, feedback, or just want to say hi, feel free to reach out to us on Discord or Twitter: + +- [Discord](https://discord.com/users/1131657390752800899) +- [Twitter](https://twitter.com/yukiarimo) + +We look forward to hearing from you! + ### Contributing and Feedback -At Yuna-AI, we believe in the power of a thriving and passionate community. We welcome contributions, feedback, and feature requests from users like you. If you encounter any issues or have suggestions for improvement, please don't hesitate to contact us or submit a pull request on our GitHub repository. +At Yuna AI, we believe in the power of a thriving and passionate community. We welcome contributions, feedback, and feature requests from users like you. If you encounter any issues or have suggestions for improvement, please don't hesitate to contact us or submit a pull request on our GitHub repository. Thank you for choosing Yuna AI as your personal AI companion. We hope you have a delightful experience with your AI girlfriend! ### License -Yuna-AI is released under the [MIT License](https://opensource.org/licenses/MIT), enabling you to freely use, modify, and distribute the software according to the terms of the license. +Yuna AI is released under the [MIT License](https://opensource.org/licenses/MIT), enabling you to freely use, modify, and distribute the software according to the terms of the license. ### Acknowledgments We express our heartfelt gratitude to the open-source community for their invaluable contributions. Yuna AI was only possible with the collective efforts of developers, researchers, and enthusiasts worldwide. \ No newline at end of file diff --git a/index.html b/index.html index be04c0b..24b8d6c 100644 --- a/index.html +++ b/index.html @@ -66,7 +66,7 @@

Yuna

-
⚙️
+
⚙️
@@ -149,29 +149,25 @@

Modes

X
-
+ +

Mode

-
-
- +
-
-
-
-
- - - -
+ +
+ + +
diff --git a/lib/dataindex.py b/lib/dataindex.py deleted file mode 100644 index 9c90ff0..0000000 --- a/lib/dataindex.py +++ /dev/null @@ -1,29 +0,0 @@ -import json - -# Open the input text file -with open('yuna-roleplay-2.txt', 'r') as file: - lines = file.read() - -# Split lines into variables -lines = lines.split("🛑\n") - -# Open a new output file for writing the JSONL data -with open('output.jsonl', 'w') as output_file: - for line in lines: - parts = line.split("🤖") - if len(parts) == 2: - user_part = parts[0].replace("👤", "") - bot_part = parts[1] - - # Create a dictionary in the desired format - data = { - "instruction": user_part, - "context": "", - "response": bot_part, - "category": "open_qa" - } - - # Write the JSON representation to the output file - output_file.write(json.dumps(data) + '\n') - -print("Conversion to JSONL format is complete!") \ No newline at end of file diff --git a/lib/gpt.py b/lib/gpt.py deleted file mode 100644 index 165f940..0000000 --- a/lib/gpt.py +++ /dev/null @@ -1,18 +0,0 @@ -from transformers import pipeline - -generator = pipeline('text-generation', model='./text/gpt-neo/1.3B/') - -while True: - input('press enter to continue...') - # read input question from file - with open('input.txt', 'r') as f: - userInput = f.read() - - maxlengthoftext = input('what is amount: ') - - # generate response - result = generator(userInput, do_sample=True, max_length=int(maxlengthoftext)) - - # write response to file - with open('output.txt', 'w') as f: - f.write(result[0]['generated_text']) \ No newline at end of file diff --git a/lib/article.py b/lib/test/article.py similarity index 100% rename from lib/article.py rename to lib/test/article.py diff --git a/test.py b/lib/test/blip.py similarity index 100% rename from test.py rename to lib/test/blip.py diff --git a/lib/c.py b/lib/test/c.py similarity index 100% rename from lib/c.py rename to lib/test/c.py diff --git a/lib/qna.py b/lib/test/qna.py similarity index 100% rename from lib/qna.py rename to lib/test/qna.py diff --git a/lib/search.py b/lib/test/search.py similarity index 100% rename from lib/search.py rename to lib/test/search.py diff --git a/lib/stable-diff.py b/lib/test/stable-diff.py similarity index 100% rename from lib/stable-diff.py rename to lib/test/stable-diff.py diff --git a/lib/translate.py b/lib/test/translate.py similarity index 100% rename from lib/translate.py rename to lib/test/translate.py diff --git a/lib/yunacpp/build-yuna.zip b/lib/yunacpp/build-yuna.zip index 4b40b15..3687055 100644 Binary files a/lib/yunacpp/build-yuna.zip and b/lib/yunacpp/build-yuna.zip differ diff --git a/requirements.txt b/requirements.txt index 8ab6294..d441ecd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -flask \ No newline at end of file +flask +flask_cors \ No newline at end of file diff --git a/static/css/index.css b/static/css/index.css index 68a97ae..934afc5 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -70,18 +70,30 @@ img { padding: 10px; border-radius: 12px; max-width: 75%; + background-color: #fff; + /* White background for all messages */ + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); + /* Add shadow for a modern look */ } .block-message-1 { + align-self: flex-start; + border-top-left-radius: 0; + /* Remove top left border radius for Yuna's messages */ + border-bottom-right-radius: 20px; + /* Add bottom right border radius for Yuna's messages */ background-color: #ffc0cb; /* Pink background for Yuna's messages */ - align-self: flex-start; } .block-message-2 { + align-self: flex-end; + border-top-right-radius: 0; + /* Remove top right border radius for Yuki's messages */ + border-bottom-left-radius: 20px; + /* Add bottom left border radius for Yuki's messages */ background-color: #add8e6; /* Light blue background for Yuki's messages */ - align-self: flex-end; } /* Text styles */ @@ -92,6 +104,29 @@ img { font-size: 16px; color: #333; text-align: left; + font-family: 'Helvetica Neue', sans-serif; + /* Use a sans-serif font for all messages */ +} + +/* Cute styles */ +.block-message-1 { + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.4); + /* Add stronger shadow for Yuna's messages */ +} + +.block-message-2 { + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.4); + /* Add stronger shadow for Yuki's messages */ +} + +.block-message-1 p { + font-size: 18px; + /* Slightly larger font size for Yuna's messages */ +} + +.block-message-2 p { + font-size: 18px; + /* Slightly larger font size for Yuki's messages */ } /* Style for the input area */ @@ -99,9 +134,23 @@ img { width: 96%; padding: 10px; font-size: 16px; - border: 1px solid #ccc; - border-radius: 5px; + border: none; + border-radius: 16px; margin-bottom: 10px; + background-color: #f0f0f0; + /* Light background color */ + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); + /* Add shadow for a modern look */ + outline: none; + /* Remove outline on focus */ + transition: all 0.3s ease-in-out; +} + +#input_text:focus { + background-color: #fff; + /* White background color on focus */ + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.4); + /* Add stronger shadow on focus */ } /* Style for the Send button */ @@ -175,4 +224,80 @@ img { #message-container { max-height: calc(100vh - 300px) !important; } +} + +.typing-bubble { + display: flex; + align-items: center; + justify-content: center; + margin-top: 20px; +} + +.dot { + width: 10px; + height: 10px; + margin: 0 5px; + background-color: #000000; + border-radius: 50%; + animation: typing 1s infinite; +} + +.dot:nth-child(2) { + animation-delay: 0.2s; +} + +.dot:nth-child(3) { + animation-delay: 0.4s; +} + +@keyframes typing { + 0% { + transform: translateY(0); + opacity: 1; + } + + 50% { + transform: translateY(-10px); + opacity: 0.5; + } + + 100% { + transform: translateY(0); + opacity: 1; + } +} + +.edit-popup { + display: flex !important; +} + +.block-popup { + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + position: fixed; + background: rgba(255, 255, 255, 0.9); + display: none; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 9999; + padding: 20px; + box-sizing: border-box; + text-align: center; +} + +.block-popup * { + margin: 10px; + font-size: 1.2rem; + text-shadow: 1px 1px 0 #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff; +} + +.block-popup img { + max-width: 100%; + border-radius: 50%; + box-shadow: 2px 2px 5px #888888; } \ No newline at end of file diff --git a/static/img/yuna-full copy.png b/static/img/yuna-full copy.png deleted file mode 100644 index 11eaf5d..0000000 Binary files a/static/img/yuna-full copy.png and /dev/null differ diff --git a/static/js/index.js b/static/js/index.js index 6dda243..9d21872 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -24,6 +24,18 @@ function sendMessage(message) { let formattedMessage = formatMessage(messageData); messageContainer.appendChild(formattedMessage); + const typingBubble = ` +
+
+
+
+
+
+
+ `; + + messageContainer.insertAdjacentHTML('beforeend', typingBubble); + scrollMsg() playAudio(audioType = 'send') @@ -47,6 +59,11 @@ function sendMessage(message) { }) .then(response => response.json()) .then(data => { + // Delete the element with id "bubble" + const bubble = document.getElementById('bubble'); + if (bubble) { + bubble.remove(); + } // Display if ok messageContainer = document.getElementById('message-container'); @@ -66,7 +83,10 @@ function sendMessage(message) { } }) .catch(error => { - //console.error('Error sending message:', error); + const bubble = document.getElementById('bubble'); + if (bubble) { + bubble.remove(); + } messageContainer = document.getElementById('message-container'); @@ -192,6 +212,7 @@ function editHistory() { .then(data => { var historyPopup = document.createElement('div'); historyPopup.classList.add('block-popup'); + historyPopup.classList.add('edit-popup'); // Create a pop-up dialog var editDialog = document.createElement('div'); @@ -200,7 +221,7 @@ function editHistory() { // Create a textarea to edit the message var editTextArea = document.createElement('textarea'); editTextArea.classList.add('block-scroll'); - editTextArea.value = JSON.stringify(data); + editTextArea.value = JSON.stringify(data, null, 2); editDialog.appendChild(editTextArea); // Create a button to save the edited message @@ -212,7 +233,7 @@ function editHistory() { // Call the function to save the edited message here, e.g., send it to the server sendEditHistory(editedText); // Remove the pop-up dialog - editDialog.remove(); + historyPopup.remove(); }); editDialog.appendChild(saveButton); @@ -244,7 +265,6 @@ function sendEditHistory(editTextArea) { .then(response => response.json()) .then(data => { // Display if ok - console.log(data.response) loadSelectedHistory() }) .catch(error => { @@ -335,36 +355,54 @@ function scrollMsg() { objDiv.scrollTop = objDiv.scrollHeight; } -// Get the video element and its container -var video = document.getElementById('localVideo'); -var videoContainer = document.querySelector('.draggable-video'); - +// code to drag the video +const localVideo = document.getElementById('localVideo'); let isDragging = false; -let offsetX, offsetY; - -// Add mousedown event listener to start dragging -video.addEventListener('mousedown', (e) => { - isDragging = true; - offsetX = e.clientX - video.getBoundingClientRect().left; - offsetY = e.clientY - video.getBoundingClientRect().top; - video.style.cursor = 'grabbing'; -}); +let currentX; +let currentY; +let initialX; +let initialY; +let xOffset = 0; +let yOffset = 0; + +localVideo.addEventListener('mousedown', dragStart); +localVideo.addEventListener('mouseup', dragEnd); +localVideo.addEventListener('mousemove', drag); + +function dragStart(e) { + initialX = e.clientX - xOffset; + initialY = e.clientY - yOffset; + + if (e.target === localVideo) { + isDragging = true; + } +} -// Add mousemove event listener to drag the video -document.addEventListener('mousemove', (e) => { - if (!isDragging) return; - var newX = e.clientX - offsetX - videoContainer.getBoundingClientRect().left; - var newY = e.clientY - offsetY - videoContainer.getBoundingClientRect().top; +function dragEnd(e) { + initialX = currentX; + initialY = currentY; - video.style.left = `${newX}px`; - video.style.top = `${newY}px`; -}); - -// Add mouseup event listener to stop dragging -document.addEventListener('mouseup', () => { isDragging = false; - video.style.cursor = 'grab'; -}); +} + +function drag(e) { + if (isDragging) { + e.preventDefault(); + + currentX = e.clientX - initialX; + currentY = e.clientY - initialY; + + xOffset = currentX; + yOffset = currentY; + + setTranslate(currentX, currentY, localVideo); + } +} + +function setTranslate(xPos, yPos, el) { + el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`; +} + // Add an event listener to the "Capture Image" button document.getElementById('capture-image').addEventListener('click', function () { @@ -485,27 +523,20 @@ for (let i = 0; i < tabElements.length; i++) { if (localStorage.getItem('config')) { data = localStorage.getItem('config') - config_data = data + config_data = JSON.parse(data) configUrl = config_data.configUrl + server_url = config_data.server } else { // Construct the full URL for the config.json file - const configUrl = `static/config.json`; + var configUrl = `static/config.json`; // Fetch the JSON data fetch(configUrl) - .then((response) => { - // Check if the response status is OK (200) - if (!response.ok) { - throw new Error(`Failed to fetch JSON: ${response.status} ${response.statusText}`); - } - - // Parse the JSON response - return response.json(); - }) + .then(response => response.json()) .then((data) => { // Handle the JSON data config_data = data - localStorage.getItem('config') + localStorage.setItem('config', JSON.stringify(config_data)); server_url = config_data.server }) .catch((error) => { @@ -513,7 +544,8 @@ if (localStorage.getItem('config')) { }); } -function configParams() { +function openConfigParams() { + OpenPopup('settings'); var config = config_data // Get the parameter container element @@ -521,43 +553,33 @@ function configParams() { // Iterate over the JSON properties and create input elements for (const key in config) { - if (config.hasOwnProperty(key)) { - const label = document.createElement('label'); - label.textContent = key; - const input = document.createElement('input'); - input.type = 'text'; - input.value = config[key]; - - // Append the label and input to the container - parameterContainer.appendChild(label); - parameterContainer.appendChild(input); - } + if (config.hasOwnProperty(key)) { + const label = document.createElement('label'); + label.textContent = key; + const input = document.createElement('input'); + input.type = 'text'; + input.value = config[key]; + + // Append the label and input to the container + parameterContainer.appendChild(label); + parameterContainer.appendChild(input); + } } - // Add an event listener to the save button - const saveButton = document.getElementById('save-button'); - saveButton.addEventListener('click', () => { - const updatedConfig = { ...config }; - for (const key in config) { - if (config.hasOwnProperty(key)) { - const input = parameterContainer.querySelector(`input[label=${key}]`); - if (input) { - updatedConfig[key] = input.value; - } - } - } - // Save the updated config to local storage - localStorage.setItem('customConfig', JSON.stringify(updatedConfig)); - }); + return parameterContainer; } -// Get the element with the ID "settingsButton" -const settingsButton = document.getElementById('settingsButton'); +function saveConfigParams() { + const parameterContainer = document.getElementById('parameter-container'); + const inputs = parameterContainer.querySelectorAll('input'); + const obj = {}; + + inputs.forEach((input) => { + const label = input.previousSibling.textContent.trim(); + obj[label] = input.value; + }); -// Add a click event listener to the "settingsButton" -settingsButton.addEventListener('click', function() { - // Call the OpenPopup function - OpenPopup('settings'); - // Call the configParams function - configParams(); -}); \ No newline at end of file + localStorage.setItem('config', JSON.stringify(obj)); + document.getElementById('parameter-container').innerHTML = ''; + PopupClose(); +} \ No newline at end of file