This commit is contained in:
Saifeddine ALOUI 2023-07-16 20:35:18 +02:00
parent c50ba16a61
commit 5fd2b1d45e
18 changed files with 1038 additions and 73 deletions

View File

@ -1,4 +1,4 @@
from lollms.console import Conversation from lollms.apps.console import Conversation
import sys import sys
class MyConversation(Conversation): class MyConversation(Conversation):
def __init__(self, cfg=None): def __init__(self, cfg=None):

View File

@ -1,4 +1,4 @@
from lollms.console import Conversation from lollms.apps.console import Conversation
import sys import sys
class MyConversation(Conversation): class MyConversation(Conversation):

View File

@ -7,6 +7,8 @@ from lollms.config import InstallOption
from lollms.helpers import trace_exception from lollms.helpers import trace_exception
from lollms.terminal import MainMenu from lollms.terminal import MainMenu
import subprocess
class LollmsApplication: class LollmsApplication:
def __init__(self, app_name:str, config:LOLLMSConfig, lollms_paths:LollmsPaths, load_binding=True, load_model=True) -> None: def __init__(self, app_name:str, config:LOLLMSConfig, lollms_paths:LollmsPaths, load_binding=True, load_model=True) -> None:
""" """
@ -20,6 +22,17 @@ class LollmsApplication:
self.mounted_personalities = [] self.mounted_personalities = []
self.personality = None self.personality = None
try:
if config.auto_update:
ASCIIColors.info("Bindings zoo found in your personal space.\nPulling last personalities zoo")
subprocess.run(["git", "-C", self.lollms_paths.bindings_zoo_path, "pull"])
# Pull the repository if it already exists
ASCIIColors.info("Personalities zoo found in your personal space.\nPulling last personalities zoo")
subprocess.run(["git", "-C", self.lollms_paths.personalities_zoo_path, "pull"])
except Exception as ex:
ASCIIColors.error("Couldn't pull zoos. Please contact the main dev on our discord channel and report the problem.")
trace_exception(ex)
if self.config.binding_name is None: if self.config.binding_name is None:
ASCIIColors.warning(f"No binding selected") ASCIIColors.warning(f"No binding selected")
ASCIIColors.info("Please select a valid model or install a new one from a url") ASCIIColors.info("Please select a valid model or install a new one from a url")

View File

@ -174,6 +174,10 @@ Participating personalities:
full_discussion = self.reset_context() full_discussion = self.reset_context()
while True: while True:
try: try:
ump = "!@>"+self.config.user_name+": " if self.config.use_user_name_in_discussions else self.personality.user_message_prefix
if self.config.use_user_name_in_discussions:
prompt = input(f"{ASCIIColors.color_green}{self.config.user_name}: {ASCIIColors.color_reset}")
else:
prompt = input(f"{ASCIIColors.color_green}You: {ASCIIColors.color_reset}") prompt = input(f"{ASCIIColors.color_green}You: {ASCIIColors.color_reset}")
if prompt == "exit": if prompt == "exit":
return return
@ -221,12 +225,12 @@ Participating personalities:
if self.personality.processor is not None and self.personality.processor_cfg["custom_workflow"]: if self.personality.processor is not None and self.personality.processor_cfg["custom_workflow"]:
full_discussion += ( full_discussion += (
self.personality.user_message_prefix + ump +
preprocessed_prompt preprocessed_prompt
) )
else: else:
full_discussion += ( full_discussion += (
self.personality.user_message_prefix + ump +
preprocessed_prompt + preprocessed_prompt +
self.personality.link_text + self.personality.link_text +
self.personality.ai_message_prefix self.personality.ai_message_prefix

130
lollms/apps/playground/.gitignore vendored Normal file
View File

@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,96 @@
# LoLLMs Playground
<div align="center">
<img src="https://github.com/ParisNeo/lollms/blob/main/lollms/assets/logo.png" alt="Logo" width="200" height="200">
</div>
![GitHub license](https://img.shields.io/github/license/ParisNeo/lollms-playground)
![GitHub issues](https://img.shields.io/github/issues/ParisNeo/lollms-playground)
![GitHub stars](https://img.shields.io/github/stars/ParisNeo/lollms-playground)
![GitHub forks](https://img.shields.io/github/forks/ParisNeo/lollms-playground)
[![Discord](https://img.shields.io/discord/1092918764925882418?color=7289da&label=Discord&logo=discord&logoColor=ffffff)](https://discord.gg/4rR282WJb6)
[![Follow me on Twitter](https://img.shields.io/twitter/follow/SpaceNerduino?style=social)](https://twitter.com/SpaceNerduino)
[![Follow Me on YouTube](https://img.shields.io/badge/Follow%20Me%20on-YouTube-red?style=flat&logo=youtube)](https://www.youtube.com/user/Parisneo)
[![pages-build-deployment](https://github.com/ParisNeo/lollms-playground/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/ParisNeo/lollms-playground/actions/workflows/pages/pages-build-deployment)
This tool provides a web-based interface to test LoLLMs endpoints and generate text using a LoLLMs server.
## Prerequisites
To use this tool, you need to have [Node.js](https://nodejs.org) installed on your machine.
## Installation
1. Clone this repository or download the source code.
```bash
git clone https://github.com/ParisNeo/lollms-playground.git
```
2. Install the dependencies.
```bash
npm install -g http-server
```
Now you are ready for some action.
## Usage
1. Start the LoLLMs server. You can use `lollms-server` to run the server with the desired configuration. Here are a few examples:
- To run the server on `localhost` and port `9600`:
```bash
lollms-server --host localhost --port 9600
```
- To run the server on a different host and port:
```bash
lollms-server --host mydomain.com --port 8080
```
- For more information on the available options, you can use the `--help` flag:
```bash
lollms-server --help
```
2. Start the web server for the LoLLMs Playground.
```bash
http-server
```
3. Open your web browser and visit `http://localhost:8081` (or the appropriate URL) to access the LoLLMs Playground.
![image](https://github.com/ParisNeo/lollms-playground/assets/827993/f69811c5-b029-4321-b31a-e4699b57ff49)
4. Press the icon to get to the connection ui.
![image](https://github.com/ParisNeo/lollms-playground/assets/827993/df7de411-8017-4f8a-a86a-c9482f62ef94)
4. Fill in the host and port fields with the appropriate values for your LoLLMs server.
5. Click the "Connect" button to establish a connection with the LoLLMs server.
![image](https://github.com/ParisNeo/lollms-playground/assets/827993/bb112985-b55a-4b31-9aa4-e42c0e703610)
6. Once connected, you can enter a prompt and click the "Generate Text" button to initiate text generation.
![image](https://github.com/ParisNeo/lollms-playground/assets/827993/6c91dc3a-887f-410f-a0ff-c10331f5a6a6)
7. The generated text will be displayed in the output section of the page.
![image](https://github.com/ParisNeo/lollms-playground/assets/827993/ae7733ab-f7aa-4fe8-8f51-33afa6ab903b)
![image](https://github.com/ParisNeo/lollms-playground/assets/827993/33e95e4e-3763-4d4c-b1a7-1e3620f416bc)
## Customization
You can customize the appearance and behavior of the tool by modifying the HTML, CSS, and JavaScript code in the `lollms_playground.html` file.
## Contributing
Contributions are welcome! If you find any issues or want to add new features, feel free to open an issue or submit a pull request.
## License
This project is licensed under the [Apache 2.0](LICENSE).
```

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<title>Logo Page</title>
<style>
html, body {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<a href="lollms_playground.html">
<img src="logo.png" alt="Logo" class="cursor-pointer">
</a>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

View File

@ -0,0 +1,388 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LoLLMs Endpoint Test</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/14.6.4/nouislider.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/14.6.4/nouislider.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<style>
.hover\:bg-blue-600:hover {
background-color: #2563eb;
}
.active\:bg-blue-700:active {
background-color: #1d4ed8;
}
/* Custom slider handle style */
.noUi-handle {
background-color: #2563eb;
border-color: #1d4ed8;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.15);
width: 5px;
height: 5px;
border-radius: 15px;
}
/* Custom slider track style */
.noUi-connect {
background-color: #2563eb;
}
/* Custom slider tooltip style */
.slider-value {
display: inline-block;
margin-left: 10px;
color: #6b7280;
font-size: 14px;
}
/* Shrink the button size */
.small-button {
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
}
</style>
</head>
<body class="bg-gray-100 w-full">
<div class="flex items-center justify-center min-h-screen w-full">
<div class="w-full bg-blue-300 p-6 rounded shadow m-4">
<h1 class="text-2xl font-bold mb-4"><img src="logo.png" alt="Logo" class="w-10 h-10 mr-2">LoLLMs Playground</h1>
<div id="connection-section">
<div class="mb-4">
<label for="host" class="block text-sm font-medium text-gray-700">Host:</label>
<input id="host" type="text" class="mt-1 p-2 border border-gray-300 rounded-md w-full"
value="localhost" />
</div>
<div class="mb-4">
<label for="port" class="block text-sm font-medium text-gray-700">Port:</label>
<input id="port" type="number" class="mt-1 p-2 border border-gray-300 rounded-md w-full" value="9601" />
</div>
<button id="connect-btn"
class="bg-blue-500 hover:bg-blue-600 active:bg-blue-700 text-white font-bold py-2 px-4 rounded">Connect</button>
<label class="hidden" id = "connecting">connecting ...</label>
</div>
<div id="generation-section" class="w-full hidden">
<div>
<button id="generate-btn" class="bg-blue-500 hover:bg-blue-600 active:bg-blue-700 text-white font-bold py-2 px-4 rounded">Generate Text</button>
<button id="stop-btn" class="bg-red-500 hover:bg-red-600 active:bg-red-700 text-white font-bold py-2 px-4 rounded hidden">Stop Generation</button>
<button id="export-btn" class="bg-green-500 hover:bg-green-600 active:bg-green-700 text-white font-bold py-2 px-4 rounded">Export Text</button>
</div>
<div class="flex flex-row">
<div class="flex-grow">
<div class="flex-grow">
<div>
<textarea id="text" class="mt-4 p-2 border border-gray-300 rounded-md h-64 overflow-y-scroll w-full" type="text"></textarea>
</div>
</div>
</div>
<div id="settings" class="w-32 ml-4">
<div class="mb-4">
<label for="temperature" class="block text-sm font-medium text-gray-700">Temperature:<span id="temperature-value" class="slider-value">1</span></label>
<div id="temperature-slider" class="mt-1 slider"></div>
</div>
<div class="mb-4">
<label for="top-k" class="block text-sm font-medium text-gray-700">Top K:<span id="top-k-value" class="slider-value">1</span></label>
<div id="top-k-slider" class="mt-1"></div>
</div>
<div class="mb-4">
<label for="top-p" class="block text-sm font-medium text-gray-700">Top P:<span id="top-p-value" class="slider-value">1</span></label>
<div id="top-p-slider" class="mt-1"></div>
</div>
<div class="mb-4">
<label for="repeat-penalty" class="block text-sm font-medium text-gray-700">Repeat Penalty:<span id="repeat-penalty-value" class="slider-value">1</span></label>
<div id="repeat-penalty-slider" class="mt-1"></div>
</div>
<div class="mb-4">
<label for="repeat-last-n" class="block text-sm font-medium text-gray-700">Repeat Last N:<span id="repeat-last-n-value" class="slider-value">1</span></label>
<div id="repeat-last-n-slider" class="mt-1"></div>
</div>
<div class="mb-4">
<label for="repeat-last-n" class="block text-sm font-medium text-gray-700">Repeat Last N:<span id="repeat-last-n-value" class="slider-value">1</span></label>
<input id="seed" class="mt-1 w-full" value="-1">
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.socket.io/4.4.1/socket.io.min.js"></script>
<script>
let temperatureValue = 1;
let topKValue = 50;
let topPValue = 0.5;
let repeatPenaltyValue = 1.6;
let repeatLastNValue = 30;
let seedValue = -1;
// Initialize sliders
const temperatureSlider = document.getElementById('temperature-slider');
const topKSlider = document.getElementById('top-k-slider');
const topPSlider = document.getElementById('top-p-slider');
const repeatPenaltySlider = document.getElementById('repeat-penalty-slider');
const repeatLastNSlider = document.getElementById('repeat-last-n-slider');
const temperatureValue_ui = document.getElementById('temperature-value');
const topKValue_ui = document.getElementById('top-k-value');
const topPValue_ui = document.getElementById('top-p-value');
const repeatPenaltyValue_ui = document.getElementById('repeat-penalty-value');
const repeatLastNValue_ui = document.getElementById('repeat-last-n-value');
temperatureValue_ui.innerText = temperatureValue
topKValue_ui.innerText = topKValue
topPValue_ui.innerText = topPValue
repeatPenaltyValue_ui.innerText = repeatPenaltyValue
repeatLastNValue_ui.innerText = repeatLastNValue
noUiSlider.create(temperatureSlider, {
start: 1,
step: 0.1,
range: {
min: 0.1,
max: 5
},
format: {
to: value => parseFloat(value.toFixed(1)),
from: value => parseFloat(value)
},
value:temperatureValue
});
noUiSlider.create(topKSlider, {
start: 50,
step: 1,
range: {
min: 1,
max: 300
},
format: {
to: value => Math.round(value),
from: value => parseFloat(value)
},
value:topKValue
});
noUiSlider.create(topPSlider, {
start: 0.5,
step: 0.01,
range: {
min: 0,
max: 1
},
format: {
to: value => parseFloat(value.toFixed(2)),
from: value => parseFloat(value)
},
value:topPValue
});
noUiSlider.create(repeatPenaltySlider, {
start: 1.5,
step: 0.1,
range: {
min: 0.3,
max: 10.0
},
format: {
to: value => parseFloat(value.toFixed(1)),
from: value => parseFloat(value)
},
value:repeatPenaltyValue
});
noUiSlider.create(repeatLastNSlider, {
start: 10,
step: 1,
range: {
min: 1,
max: 100
},
format: {
to: value => Math.round(value),
from: value => parseFloat(value)
},
value:repeatLastNValue
});
// Event handler for temperature slider value change
temperatureSlider.noUiSlider.on('change', (values, handle) => {
temperatureValue = parseFloat(values[handle]);
temperatureValue_ui.innerText = temperatureValue
// Update the temperature value in your code or send it to the server
console.log('Temperature:', temperatureValue);
});
// Event handler for top-k slider value change
topKSlider.noUiSlider.on('change', (values, handle) => {
topKValue = Math.round(values[handle]);
topKValue_ui.innerText = topKValue
// Update the top-k value in your code or send it to the server
console.log('Top K:', topKValue);
});
// Event handler for top-p slider value change
topPSlider.noUiSlider.on('change', (values, handle) => {
topPValue = parseFloat(values[handle]);
topPValue_ui.innerText = topPValue
// Update the top-p value in your code or send it to the server
console.log('Top P:', topPValue);
});
// Event handler for repeat penalty slider value change
repeatPenaltySlider.noUiSlider.on('change', (values, handle) => {
repeatPenaltyValue = parseFloat(values[handle]);
repeatPenaltyValue_ui.innerText = repeatPenaltyValue
console.log('Repeat Penalty:', repeatPenaltyValue);
});
// Event handler for repeat last N slider value change
repeatLastNSlider.noUiSlider.on('change', (values, handle) => {
repeatLastNValue = Math.round(values[handle]);
repeatLastNValue_ui.innerText = repeatLastNValue
console.log('Repeat Last N:', repeatLastNValue);
});
const socket = io();
const connectButton = document.getElementById('connect-btn');
const generateButton = document.getElementById('generate-btn');
const stopButton = document.getElementById('stop-btn');
const connectionSection = document.getElementById('connection-section');
const generationSection = document.getElementById('generation-section');
const connectingText = document.getElementById('connecting');
// Append the received chunks to the text div
function appendToOutput(chunk) {
const outputDiv = document.getElementById('text');
outputDiv.value += chunk;
outputDiv.scrollTop = outputDiv.scrollHeight;
}
// Event handler for receiving generated text chunks
socket.on('text_chunk', data => {
console.log('Received chunk:', data.chunk);
appendToOutput(data.chunk);
});
// Event handler for receiving generated text chunks
socket.on('text_generated', data => {
console.log('text generated:', data.text);
// Toggle button visibility
generateButton.classList.remove('hidden');
stopButton.classList.add('hidden');
});
socket.on('generation_error', data => {
console.log('generation_error:', data);
// Toggle button visibility
generateButton.classList.remove('hidden');
stopButton.classList.add('hidden');
});
// Event handler for successful connection
socket.on('connect', () => {
console.log('Connected to LoLLMs server');
connectButton.disabled = true;
connectingText.classList.add("hidden")
connectionSection.classList.add('hidden');
generationSection.classList.remove('hidden');
});
// Event handler for error during text generation
socket.on('buzzy', error => {
console.error('Server is busy. Wait for your turn', error);
const outputDiv = document.getElementById('text');
outputDiv.value += `<p class="text-red-500">Error: ${error.message}</p>`;
outputDiv.scrollTop = outputDiv.scrollHeight;
// Toggle button visibility
generateButton.classList.remove('hidden');
stopButton.classList.add('hidden');
});
// Event handler for error during text generation
socket.on('generation_canceled', error => {
// Toggle button visibility
generateButton.classList.remove('hidden');
stopButton.classList.add('hidden');
console.log("Generation canceled OK")
});
// Triggered when the "Connect" button is clicked
connectButton.addEventListener('click', () => {
const hostInput = document.getElementById('host');
const portInput = document.getElementById('port');
const host = hostInput.value.trim();
const port = parseInt(portInput.value);
if (host && port) {
socket.io.uri = `http://${host}:${port}`;
socket.connect();
connectingText.classList.remove("hidden")
}
});
// Triggered when the "Generate Text" button is clicked
generateButton.addEventListener('click', () => {
const outputDiv = document.getElementById('text');
var prompt = outputDiv.value
console.log(prompt)
// Trigger the 'generate_text' event with the prompt
socket.emit('generate_text', { prompt, personality: -1, n_predicts: 1024 ,
parameters: {
temperature: temperatureValue,
top_k: topKValue,
top_p: topPValue,
repeat_penalty: repeatPenaltyValue, // Update with desired repeat penalty value
repeat_last_n: repeatLastNValue, // Update with desired repeat_last_n value
seed: parseInt(seedValue)
}});
// Toggle button visibility
generateButton.classList.add('hidden');
stopButton.classList.remove('hidden');
});
// Triggered when the "Stop Generation" button is clicked
stopButton.addEventListener('click', () => {
// Trigger the 'cancel_generation' event
socket.emit('cancel_generation',{});
});
// Function to export text as a text file
function exportTextToFile() {
const outputDiv = document.getElementById('text');
const textToExport = outputDiv.value;
const element = document.createElement('a');
const file = new Blob([textToExport], {type: 'text/plain'});
element.href = URL.createObjectURL(file);
element.download = 'exported_text.txt';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
// Triggered when the "Export Text" button is clicked
const exportButton = document.getElementById('export-btn');
exportButton.addEventListener('click', exportTextToFile);
</script>
</body>
</html>

View File

@ -0,0 +1,5 @@
{
"dependencies": {
"http-server": "^14.1.1"
}
}

View File

@ -8,9 +8,9 @@ from lollms.main_config import LOLLMSConfig
from lollms.binding import LLMBinding, BindingBuilder, ModelBuilder from lollms.binding import LLMBinding, BindingBuilder, ModelBuilder
from lollms.personality import PersonalityBuilder from lollms.personality import PersonalityBuilder
from lollms.helpers import ASCIIColors from lollms.helpers import ASCIIColors
from lollms.console import MainMenu from lollms.apps.console import MainMenu
from lollms.paths import LollmsPaths from lollms.paths import LollmsPaths
from lollms.console import MainMenu from lollms.apps.console import MainMenu
from lollms.app import LollmsApplication from lollms.app import LollmsApplication
from typing import List, Tuple from typing import List, Tuple
import importlib import importlib
@ -334,6 +334,12 @@ class LoLLMsServer(LollmsApplication):
txt = self.model.detokenize(prompt) txt = self.model.detokenize(prompt)
emit("detokenized", {"text":txt}) emit("detokenized", {"text":txt})
@self.socketio.on('embed')
def detokenize(data):
prompt = data['prompt']
txt = self.model.embed(prompt)
emit("embeded", {"text":txt})
@self.socketio.on('cancel_generation') @self.socketio.on('cancel_generation')
def cancel_generation(data): def cancel_generation(data):
client_id = request.sid client_id = request.sid
@ -415,6 +421,7 @@ class LoLLMsServer(LollmsApplication):
else: else:
try: try:
personality: AIPersonality = self.personalities[personality_id] personality: AIPersonality = self.personalities[personality_id]
ump = "!@>"+self.config.user_name+": " if self.config.use_user_name_in_discussions else self.personality.user_message_prefix
personality.model = model personality.model = model
cond_tk = personality.model.tokenize(personality.personality_conditioning) cond_tk = personality.model.tokenize(personality.personality_conditioning)
n_cond_tk = len(cond_tk) n_cond_tk = len(cond_tk)
@ -432,12 +439,12 @@ class LoLLMsServer(LollmsApplication):
preprocessed_prompt = prompt preprocessed_prompt = prompt
if personality.processor is not None and personality.processor_cfg["custom_workflow"]: if personality.processor is not None and personality.processor_cfg["custom_workflow"]:
full_discussion_blocks.append(personality.user_message_prefix) full_discussion_blocks.append(ump)
full_discussion_blocks.append(preprocessed_prompt) full_discussion_blocks.append(preprocessed_prompt)
else: else:
full_discussion_blocks.append(personality.user_message_prefix) full_discussion_blocks.append(ump)
full_discussion_blocks.append(preprocessed_prompt) full_discussion_blocks.append(preprocessed_prompt)
full_discussion_blocks.append(personality.link_text) full_discussion_blocks.append(personality.link_text)
full_discussion_blocks.append(personality.ai_message_prefix) full_discussion_blocks.append(personality.ai_message_prefix)

View File

@ -8,7 +8,7 @@ from pathlib import Path
import argparse import argparse
from tqdm import tqdm from tqdm import tqdm
from lollms.personality import PersonalityBuilder from lollms.personality import PersonalityBuilder
from lollms.console import MainMenu from lollms.apps.console import MainMenu
from lollms.app import LollmsApplication from lollms.app import LollmsApplication

View File

@ -1,5 +1,5 @@
# =================== Lord Of Large Language Models Configuration file =========================== # =================== Lord Of Large Language Models Configuration file ===========================
version: 6 version: 9
binding_name: null binding_name: null
model_name: null model_name: null
@ -24,5 +24,12 @@ personalities: ["english/generic/lollms"]
active_personality_id: 0 active_personality_id: 0
override_personality_model_parameters: false #if true the personality parameters are overriden by those of the configuration (may affect personality behaviour) override_personality_model_parameters: false #if true the personality parameters are overriden by those of the configuration (may affect personality behaviour)
# User infos
user_name: user user_name: user
user_description: ""
use_user_name_in_discussions: false
user_avatar: default_user
# Automatic update
auto_update: false
debug: false

View File

@ -82,19 +82,11 @@ class LollmsPaths:
# Clone the repository to the target path # Clone the repository to the target path
ASCIIColors.info("No bindings found in your personal space.\nCloning the personalities zoo") ASCIIColors.info("No bindings found in your personal space.\nCloning the personalities zoo")
subprocess.run(["git", "clone", bindings_zoo_repo, self.bindings_zoo_path]) subprocess.run(["git", "clone", bindings_zoo_repo, self.bindings_zoo_path])
else:
# Pull the repository if it already exists
ASCIIColors.info("Bindings zoo found in your personal space.\nPulling last personalities zoo")
subprocess.run(["git", "-C", self.bindings_zoo_path, "pull"])
if not self.personalities_zoo_path.exists(): if not self.personalities_zoo_path.exists():
# Clone the repository to the target path # Clone the repository to the target path
ASCIIColors.info("No personalities found in your personal space.\nCloning the personalities zoo") ASCIIColors.info("No personalities found in your personal space.\nCloning the personalities zoo")
subprocess.run(["git", "clone", personalities_zoo_repo, self.personalities_zoo_path]) subprocess.run(["git", "clone", personalities_zoo_repo, self.personalities_zoo_path])
else:
# Pull the repository if it already exists
ASCIIColors.info("Personalities zoo found in your personal space.\nPulling last personalities zoo")
subprocess.run(["git", "-C", self.personalities_zoo_path, "pull"])
def copy_default_config(self): def copy_default_config(self):
@ -156,7 +148,7 @@ class LollmsPaths:
except Exception as ex: except Exception as ex:
print(f"{ASCIIColors.color_red}Global paths configuration file found but seems to be corrupted{ASCIIColors.color_reset}") print(f"{ASCIIColors.color_red}Global paths configuration file found but seems to be corrupted{ASCIIColors.color_reset}")
print("Couldn't find your personal data path!") print("Couldn't find your personal data path!")
cfg.lollms_path = str(Path(__file__).parent) cfg.lollms_path = lollms_path
cfg["lollms_personal_path"] = str(Path.home()/"Documents/lollms") cfg["lollms_personal_path"] = str(Path.home()/"Documents/lollms")
print("Please specify the folder where your configuration files, your models and your custom personalities need to be stored:") print("Please specify the folder where your configuration files, your models and your custom personalities need to be stored:")
lollms_personal_path = input(f"Folder path: ({cfg.lollms_personal_path}):") lollms_personal_path = input(f"Folder path: ({cfg.lollms_personal_path}):")
@ -173,7 +165,7 @@ class LollmsPaths:
cfg = BaseConfig() cfg = BaseConfig()
cfg.load_config(global_paths_cfg_path) cfg.load_config(global_paths_cfg_path)
try: try:
lollms_path = cfg.lollms_path # lollms_path = cfg.lollms_path
lollms_personal_path = cfg.lollms_personal_path lollms_personal_path = cfg.lollms_personal_path
return LollmsPaths(global_paths_cfg_path, lollms_path, lollms_personal_path, custom_default_cfg_path=custom_default_cfg_path, tool_prefix=tool_prefix) return LollmsPaths(global_paths_cfg_path, lollms_path, lollms_personal_path, custom_default_cfg_path=custom_default_cfg_path, tool_prefix=tool_prefix)
except Exception as ex: except Exception as ex:

View File

@ -974,6 +974,17 @@ class APScript(StateMachine):
ASCIIColors.blue("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*") ASCIIColors.blue("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*")
@staticmethod
def reinstall_pytorch_with_cuda():
result = subprocess.run(["pip", "install", "--upgrade", "torch", "torchvision", "torchaudio", "--no-cache-dir", "--index-url", "https://download.pytorch.org/whl/cu117"])
if result.returncode != 0:
ASCIIColors.warning("Couldn't find Cuda build tools on your PC. Reverting to CPU.")
result = subprocess.run(["pip", "install", "--upgrade", "torch", "torchvision", "torchaudio", "--no-cache-dir"])
if result.returncode != 0:
ASCIIColors.error("Couldn't install pytorch !!")
else:
ASCIIColors.error("Pytorch installed successfully!!")
def add_file(self, path, callback=None): def add_file(self, path, callback=None):
self.callback=callback self.callback=callback
self.files.append(path) self.files.append(path)

View File

@ -14,11 +14,146 @@ import pkg_resources
from pathlib import Path from pathlib import Path
import yaml import yaml
import sys import sys
class Menu:
"""Console menu tool that allows the user to select options."""
class MainMenu: def __init__(self, name, options):
"""
Initialize a new menu instance.
Parameters:
name (str): The name of the menu.
options (list): A list of menu options, each represented as a dictionary
with 'name' as the option display name, 'fn' as the function
to be called when the user selects the option, an optional
'help' message to display a brief description of the option,
and an optional 'exit_after_exec' flag to exit the menu
automatically after executing the function.
"""
self.name = name
self.options = options
def show(self, menu_structure=None):
"""
Display the menu options to the user and handle the user's choice.
Parameters:
menu_structure (list, optional): A list of menu options to use instead of
the ones sent to the constructor. If None,
it uses the options sent to the constructor.
Note: If both `menu_structure` and options sent to the constructor are None,
the method will raise a ValueError.
"""
if menu_structure is None and not self.options:
raise ValueError("Menu options not provided.")
if menu_structure is not None:
current_options = menu_structure
else:
current_options = self.options
while True:
ASCIIColors.cyan(f"\n--- {self.name.upper()} MENU ---")
for i, option in enumerate(current_options, start=1):
ASCIIColors.yellow(f"{i}.",end="")
print(f" {option['name']}")
ASCIIColors.yellow("0.",end="")
print(" Go back to the previous menu" if self.name != "Main" else "0. Exit")
print("help")
choice = input("Select an option: ")
if choice.isdigit():
choice = int(choice)
if 0 <= choice <= len(current_options):
if choice == 0:
if self.name == "Main":
print("Exiting the menu.")
break
else:
return
else:
chosen_option = current_options[choice - 1]
sub_menu = chosen_option.get('sub_menu')
exit_after_exec = chosen_option.get('exit_after_exec', False)
if sub_menu:
self.show(sub_menu)
else:
chosen_option['fn']()
if exit_after_exec:
print(f"Exiting {self.name.upper()} menu.")
return
else:
print("Invalid option. Please select again.")
elif choice.lower() == "help":
self.display_help(current_options)
else:
print("Invalid input. Please enter a number or 'help' for assistance.")
def display_help(self, options):
"""
Display a brief description of each option available in the menu.
Parameters:
options (list): The list of menu options to display the help for.
"""
print(f"\n--- {self.name.upper()} MENU HELP ---")
for option in options:
help_msg = option.get('help', "No help available.")
print(f"{option['name']}: {help_msg}")
def yes_no_question(self, question):
"""
Ask the user a yes or no question and wait for a valid response.
Parameters:
question (str): The question to be displayed to the user.
Returns:
bool: True if the user answers 'yes', False if the user answers 'no'.
"""
while True:
response = input(f"{question} (yes/no): ").lower()
if response in ['y', 'yes']:
return True
elif response in ['n', 'no']:
return False
else:
print("Invalid response. Please answer with 'yes' or 'no' (or 'y'/'n').")
class MainMenu(Menu):
def __init__(self, lollms_app:'LollmsApplication'): def __init__(self, lollms_app:'LollmsApplication'):
self.binding_infs = [] self.binding_infs = []
self.lollms_app = lollms_app self.lollms_app = lollms_app
main_menu_options = [
{'name': 'Main settings', 'fn': self.main_settings, 'help': "Show main settings."},
{'name': 'Select Binding', 'fn': self.select_binding, 'help': "Choose a binding."},
{'name': 'Select Model', 'fn': self.select_model, 'help': "Choose a model."},
{'name': 'View mounted Personalities', 'fn': self.vew_mounted_personalities, 'help': "View all currently mounted personalities."},
{'name': 'Mount Personality', 'fn': self.mount_personality, 'help': "Mount a new personality."},
{'name': 'Unmount Personality', 'fn': self.unmount_personality, 'help': "Unmount a personality."},
{'name': 'Select Personality', 'fn': self.select_personality, 'help': "Choose a personality."},
{'name': 'Reinstall Binding', 'fn': self.reinstall_binding, 'help': "Reinstall the selected binding."},
{'name': 'Reinstall current Personality', 'fn': self.reinstall_personality, 'help': "Reinstall the current selected personality."},
{'name': 'Reset all installs', 'fn': self.lollms_app.reset_all_installs, 'help': "Reset all installed personalities."},
{'name': 'Reset paths', 'fn': self.lollms_app.lollms_paths.resetPaths, 'help': "Reset all paths to default."},
]
super().__init__("Lollms Main menu", main_menu_options)
def main_settings(self):
self.show([
{'name': 'Set user name', 'fn': self.set_user_name, 'help': "Sets the user name."},
{'name': 'Set use user name in discussion', 'fn': self.set_use_user_name_in_discussions, 'help': "Sets the user name."},
])
def set_user_name(self):
print(f"Current user name : {self.lollms_app.config.user_name}")
self.lollms_app.config.user_name = input("New user name:")
self.lollms_app.config.save_config()
def set_use_user_name_in_discussions(self):
ASCIIColors.info(f"Current status: {self.lollms_app.config.use_user_name_in_discussions}")
self.lollms_app.config.use_user_name_in_discussions = self.yes_no_question('Use user name in dicsussion')
self.lollms_app.config.save_config()
def show_logo(self): def show_logo(self):
print(f"{ASCIIColors.color_bright_yellow}") print(f"{ASCIIColors.color_bright_yellow}")
@ -284,51 +419,5 @@ class MainMenu:
self.select_model() self.select_model()
def main_menu(self): def main_menu(self):
while True: self.show()
print("\nMain Menu:")
print(f"{ASCIIColors.color_green}1 -{ASCIIColors.color_reset} Select Binding")
print(f"{ASCIIColors.color_green}2 -{ASCIIColors.color_reset} Select Model")
print(f"{ASCIIColors.color_green}3 -{ASCIIColors.color_reset} View mounted Personalities")
print(f"{ASCIIColors.color_green}4 -{ASCIIColors.color_reset} Mount Personality")
print(f"{ASCIIColors.color_green}5 -{ASCIIColors.color_reset} Unmount Personality")
print(f"{ASCIIColors.color_green}6 -{ASCIIColors.color_reset} Select Personality")
print(f"{ASCIIColors.color_green}7 -{ASCIIColors.color_reset} Reinstall Binding")
print(f"{ASCIIColors.color_green}8 -{ASCIIColors.color_reset} Reinstall current Personality")
print(f"{ASCIIColors.color_green}9 -{ASCIIColors.color_reset} Reset all installs")
print(f"{ASCIIColors.color_green}10 -{ASCIIColors.color_reset} Reset paths")
print(f"{ASCIIColors.color_green}11 -{ASCIIColors.color_reset} Back to app")
print(f"{ASCIIColors.color_green}12 -{ASCIIColors.color_reset} Help")
print(f"{ASCIIColors.color_green}13 -{ASCIIColors.color_reset} Exit app")
choice = input("Enter your choice: ").strip()
if choice == "1":
self.select_binding()
elif choice == "2":
self.select_model()
elif choice == "3":
self.vew_mounted_personalities()
elif choice == "4":
self.mount_personality()
elif choice == "5":
self.unmount_personality()
elif choice == "6":
self.select_personality()
elif choice == "7":
self.reinstall_binding()
elif choice == "8":
self.reinstall_personality()
elif choice == "9":
self.lollms_app.reset_all_installs()
elif choice == "10":
self.lollms_app.reset_paths()
elif choice == "11":
print("Back to main app...")
break
elif choice == "12":
self.show_commands_list()
elif choice == "13":
print("Bye")
sys.exit(0)
else:
print("Invalid choice! Try again.")

View File

@ -26,7 +26,7 @@ def get_all_files(path):
setuptools.setup( setuptools.setup(
name="lollms", name="lollms",
version="2.1.39", version="2.1.40",
author="Saifeddine ALOUI", author="Saifeddine ALOUI",
author_email="aloui.saifeddine@gmail.com", author_email="aloui.saifeddine@gmail.com",
description="A python library for AI personality definition", description="A python library for AI personality definition",
@ -38,9 +38,9 @@ setuptools.setup(
install_requires=requirements, install_requires=requirements,
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'lollms-server = lollms.server:main', 'lollms-server = lollms.apps.server:main',
'lollms-console = lollms.console:main', 'lollms-console = lollms.apps.console:main',
'lollms-settings = lollms.settings:main', 'lollms-settings = lollms.apps.settings:main',
], ],
}, },
extras_require={"dev": requirements_dev}, extras_require={"dev": requirements_dev},