added lib folder and model download at start... hope it works
All checks were successful
Gitea Docker Redeploy / Redploy-App-on-self-via-SSH (push) Successful in 19s

This commit is contained in:
Tobias Weise 2024-08-20 17:47:33 +02:00
parent 973821ca4f
commit 579b76ebd5
15 changed files with 159 additions and 167 deletions

View File

@ -57,16 +57,18 @@ from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
#----------home grown-------------- #----------home grown--------------
#from scraper import WebScraper from lib.funcs import group_by
from funcs import group_by from lib.elastictools import get_by_id, update_by_id, delete_by_id, wait_for_elasticsearch
from elastictools import get_by_id, update_by_id, delete_by_id from lib.models import init_indicies, QueryLog, Chatbot, User, Text
from models import init_indicies, QueryLog, Chatbot, User, Text from lib.chatbot import ask_bot, train_text, download_llm
from lib.speech import text_to_speech
from lib.mail import send_mail
from lib.user import hash_password, create_user, create_default_users
from chatbot import ask_bot, train_text
from speech import text_to_speech
BOT_ROOT_PATH = os.getenv("BOT_ROOT_PATH") BOT_ROOT_PATH = os.getenv("BOT_ROOT_PATH")
assert BOT_ROOT_PATH
# JWT Bearer Sample # JWT Bearer Sample
jwt = { jwt = {
@ -151,35 +153,11 @@ def uses_jwt(required=True):
kwargs["user"] = user kwargs["user"] = user
return f(*args, **kwargs) return f(*args, **kwargs)
return decorated_route return decorated_route
return non_param_deco return non_param_deco
def create_key(salt: str, user_email: str) -> Fernet:
"""
Example salt: 9c46f833b3376c5f3b64d8a93951df4b
Fernet usage: token = f.encrypt(b"Secret message!")
"""
salt_bstr = bytes(salt, "utf-8")
email_bstr = bytes(user_email, "utf-8")
#password = b"password"
#salt = os.urandom(16)
#salt = b"9c46f833b3376c5f3b64d8a93951df4b"
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt_bstr,
iterations=48,
)
key = base64.urlsafe_b64encode(kdf.derive(email_bstr))
return Fernet(key)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['CORS_HEADERS'] = 'Content-Type'
app.config['CORS_METHODS'] = ["GET,POST,OPTIONS,DELETE,PUT"]
env_to_conf = { env_to_conf = {
@ -200,9 +178,6 @@ for env_key, conf_key in env_to_conf.items():
#from flask_cors import CORS #falls cross-orgin verwendet werden soll
#CORS(app)
socket = SocketIO(app, cors_allowed_origins="*") socket = SocketIO(app, cors_allowed_origins="*")
@socket.on('connect') @socket.on('connect')
@ -237,11 +212,6 @@ def handle_message(message):
socket.emit('backend token', {'done': True}, to=room) socket.emit('backend token', {'done': True}, to=room)
def hash_password(s: str) -> str:
return hashlib.md5(s.encode('utf-8')).hexdigest()
#======================= TAGS ============================= #======================= TAGS =============================
not_implemented_tag = Tag(name='Not implemented', description='Functionality not yet implemented beyond an empty response') not_implemented_tag = Tag(name='Not implemented', description='Functionality not yet implemented beyond an empty response')
@ -297,10 +267,6 @@ def login(form: LoginRequest):
}), 400 }), 400
from mail import send_mail
class RegisterRequest(BaseModel): class RegisterRequest(BaseModel):
email: str = Field(None, description='The users E-Mail that serves as nick too.') email: str = Field(None, description='The users E-Mail that serves as nick too.')
password: str = Field(None, description='A short text by the user explaining the rating.') password: str = Field(None, description='A short text by the user explaining the rating.')
@ -362,7 +328,6 @@ def register(form: RegisterRequest):
class GetSpeechRequest(BaseModel): class GetSpeechRequest(BaseModel):
text: str = Field(None, description="Some text to convert to mp3") text: str = Field(None, description="Some text to convert to mp3")
@app.post('/text2speech', summary="", tags=[], security=security) @app.post('/text2speech', summary="", tags=[], security=security)
def text2speech(form: GetSpeechRequest): def text2speech(form: GetSpeechRequest):
file_name = text_to_speech(form.text) file_name = text_to_speech(form.text)
@ -538,7 +503,10 @@ def query_bot(query: AskBotRequest, decoded_jwt, user):
] ]
) )
embeddings = OllamaEmbeddings(model="llama3", base_url="http://ollama:11434") ollama_url = os.getenv("OLLAMA_URI")
embeddings = OllamaEmbeddings(model="llama3", base_url=ollama_url)
vector_store = ElasticsearchStore( vector_store = ElasticsearchStore(
es_url=app.config['elastic_uri'], es_url=app.config['elastic_uri'],
@ -551,7 +519,7 @@ def query_bot(query: AskBotRequest, decoded_jwt, user):
bot = Chatbot.get(id=bot_id) bot = Chatbot.get(id=bot_id)
llm = Ollama( llm = Ollama(
model=bot.llm_model, model=bot.llm_model,
base_url="http://ollama:11434" base_url=ollama_url
) )
k = 4 k = 4
@ -598,7 +566,6 @@ def query_bot(query: AskBotRequest, decoded_jwt, user):
#-----------------Embedding---------------------- #-----------------Embedding----------------------
#ESDocument = namedtuple('Document', ['page_content', 'metadata'])
class TrainTextRequest(BaseModel): class TrainTextRequest(BaseModel):
bot_id: str = Field(None, description="The bot's id") bot_id: str = Field(None, description="The bot's id")
@ -627,14 +594,11 @@ def upload(form: TrainTextRequest, decoded_jwt, user):
'message': 'No data source found' 'message': 'No data source found'
}), 400 }), 400
train_text(bot_id, text) train_text(bot_id, text)
return jsonify({ return jsonify({
"status": "success" "status": "success"
}) })
#-------- non api routes ------------- #-------- non api routes -------------
@app.route("/") #Index Verzeichnis @app.route("/") #Index Verzeichnis
@ -648,79 +612,34 @@ def catchAll(path):
return send_from_directory('./public', path) return send_from_directory('./public', path)
#def init_indicies():
# # create the mappings in elasticsearch
# for Index in [QueryLog, Chatbot, User, Text]:
# Index.init()
def create_user(email, password, role="user", verified=False):
user = User(meta={'id': email}, email=email, password_hash=hash_password(password + email), role=role)
user.creation_date = datetime.now()
user.isEmailVerified = verified
user.save()
return user
def create_default_users():
#create default users
client = Elasticsearch(app.config['elastic_uri'])
default_users = os.getenv("DEFAULT_USERS")
if default_users:
for (email, pwd, role) in json.loads(default_users):
if len(get_by_id(client, index="user", id_field_name="email", id_value=email)) == 0:
create_user(email, pwd, role=role, verified=True)
import requests
if __name__ == '__main__': if __name__ == '__main__':
#TODO: implement some kind of logging mechanism LOG_LEVEL = os.getenv("LOG_LEVEL")
if LOG_LEVEL:
logging.basicConfig(level=eval("logging." + LOG_LEVEL))
else:
logging.basicConfig(level=logging.WARN) logging.basicConfig(level=logging.WARN)
#TODO: implement some kind of logging mechanism
""" """
USE_LOKI_LOGGER = os.getenv("USE_LOKI_LOGGER") USE_LOKI_LOGGER = os.getenv("USE_LOKI_LOGGER")
if USE_LOKI_LOGGER: if USE_LOKI_LOGGER:
handler = logging_loki.LokiHandler( handler = logging_loki.LokiHandler(
url="http://loki:3100/loki/api/v1/push", url="http://loki:3100/loki/api/v1/push",
tags={"application": "Nextsearch"}, tags={"application": "CreativeBots"},
#auth=("username", "password"), #auth=("username", "password"),
version="1", version="1",
) )
app.logger.addHandler(handler) app.logger.addHandler(handler)
""" """
#TODO: find a clean way to wait without exceptions! wait_for_elasticsearch()
#Wait for elasticsearch to start up! download_llm()
i = 1
while True:
try:
#client = Elasticsearch(app.config['elastic_uri'])
connections.create_connection(hosts=app.config['elastic_uri'])
connections.get_connection().cluster.health(wait_for_status='yellow')
init_indicies()
print("Elasticsearch found! Run Flask-app!", flush=True)
break
except:
#except ConnectionError:
i *= 1.5
time.sleep(i)
print("Elasticsearch not found! Wait %s seconds!" % i, flush=True)
connections.create_connection(hosts=app.config['elastic_uri'], request_timeout=60) connections.create_connection(hosts=app.config['elastic_uri'], request_timeout=60)
init_indicies() init_indicies()
create_default_users() create_default_users()
app.run(debug=True, threaded=True, host='0.0.0.0') app.run(debug=False, threaded=True, host='0.0.0.0')

View File

@ -1,5 +0,0 @@
BOT_ROOT_PATH=/
ELASTIC_URI=http://elasticsearch:9200
OLLAMA_URI=http://ollama:11434

0
backend/lib/__init__.py Normal file
View File

View File

@ -1,6 +1,7 @@
"""
All functions around bots
from models import Chatbot, Text, User """
from uuid import uuid4 from uuid import uuid4
from collections import namedtuple from collections import namedtuple
import os, hashlib, traceback, logging import os, hashlib, traceback, logging
@ -24,12 +25,18 @@ from langchain_community.embeddings import OllamaEmbeddings
from langchain_elasticsearch import ElasticsearchStore from langchain_elasticsearch import ElasticsearchStore
from langchain.chains import create_retrieval_chain from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate from langchain_core.prompts import ChatPromptTemplate
from lib.models import Chatbot, Text, User
ollama_url = os.getenv("OLLAMA_URI") ollama_url = os.getenv("OLLAMA_URI")
elastic_url = os.getenv("ELASTIC_URI") elastic_url = os.getenv("ELASTIC_URI")
@ -145,7 +152,7 @@ def ask_bot2(question, bot_id):
] ]
) )
embeddings = OllamaEmbeddings(model="llama3", base_url="http://ollama:11434") embeddings = OllamaEmbeddings(model="llama3", base_url=ollama_url)
vector_store = ElasticsearchStore( vector_store = ElasticsearchStore(
es_url=app.config['elastic_uri'], es_url=app.config['elastic_uri'],
@ -158,7 +165,7 @@ def ask_bot2(question, bot_id):
bot = Chatbot.get(id=bot_id) bot = Chatbot.get(id=bot_id)
llm = Ollama( llm = Ollama(
model=bot.llm_model, model=bot.llm_model,
base_url="http://ollama:11434" base_url=ollama_url
) )
k = 4 k = 4
@ -203,10 +210,19 @@ def ask_bot2(question, bot_id):
from ollama import Client as OllamaClient
def download_llm(model="llama3"):
#print(ollama_url, flush=True)
#ollama_client = OllamaClient(host=ollama_url)
#x = ollama_client.pull('llama3')
#print( type(x), flush=True)
#print( x.__dict__, flush=True)
#print( x, flush=True)
s = """curl %s/api/pull -d '{ "name": "%s" }' """ % (ollama_url, model)
print( os.system(s.strip()) ,flush=True)

View File

@ -1,8 +1,8 @@
""" """
Some helper functions to make querying easier Some helper functions to make querying easier
""" """
import time, json, os
from typing import Any, Tuple, List, Dict, Any, Callable, Optional from typing import Any, Tuple, List, Dict, Any, Callable, Optional
import json
from elasticsearch import NotFoundError, Elasticsearch # for normal read/write without vectors from elasticsearch import NotFoundError, Elasticsearch # for normal read/write without vectors
from elasticsearch_dsl import Search, A, UpdateByQuery, Document, Date, Integer, Keyword, Float, Long, Text, connections from elasticsearch_dsl import Search, A, UpdateByQuery, Document, Date, Integer, Keyword, Float, Long, Text, connections
@ -47,7 +47,6 @@ def delete_by_id(client: Elasticsearch, index: str, id_field_name: str, id_value
response = s.delete() response = s.delete()
#if not response.success(): #if not response.success():
# raise Exception("Unable to delete id '%s' in index '%' !" % (index, id_value)) # raise Exception("Unable to delete id '%s' in index '%' !" % (index, id_value))
print(response, flush=True) print(response, flush=True)
@ -78,6 +77,26 @@ def get_type_schema(client: Elasticsearch):
def wait_for_elasticsearch():
#TODO: find a clean way to wait without exceptions!
#Wait for elasticsearch to start up!
elastic_url = os.getenv("ELASTIC_URI")
assert elastic_url
i = 1
while True:
try:
client = Elasticsearch(hosts=elastic_url)
#connections.create_connection(hosts=app.config['elastic_uri'])
#connections.get_connection().cluster.health(wait_for_status='yellow')
#init_indicies()
print("Elasticsearch found! Run Flask-app!", flush=True)
break
except:
#except ConnectionError:
i *= 2 #1.5
time.sleep(i)
print("Elasticsearch not found! Wait %s seconds!" % i, flush=True)

View File

@ -138,27 +138,7 @@ class QueryLog(Document):
def init_indicies(): def init_indicies():
# create the mappings in elasticsearch # create the mappings in elasticsearch
for Index in [QueryLog, Chatbot, User, Text]: for Index in [QueryLog, Chatbot, User, Text]:
Index.init() Index.init()
if __name__ == "__main__":
elastic_uri = os.getenv("ELASTIC_URI")
#elastic_uri = "http://localhost:9200"
# create and save and article
#article = Article(meta={'id': 42}, title='Hello world!', tags=['test'])
#article.body = ''' looong text '''
##article.published_from = datetime.now()
#article.save()
#article = Article.get(id=42)
#print(article.is_published())
# Display cluster health
#print(connections.get_connection().cluster.health())

View File

@ -8,6 +8,8 @@ import pyttsx3
flite -voice slt -t "This example is useful when there is a need to convert the contents of a file to speech. It can simplify tasks such as reading out the contents of a document or generating voiceovers for specific text files." flite -voice slt -t "This example is useful when there is a need to convert the contents of a file to speech. It can simplify tasks such as reading out the contents of a document or generating voiceovers for specific text files."
""" """
#espeak -v mb-en1 -s 120 "Hello world"
def text_to_speech(text: str) -> str: def text_to_speech(text: str) -> str:
unix_timestamp = datetime.now().timestamp() unix_timestamp = datetime.now().timestamp()

42
backend/lib/user.py Normal file
View File

@ -0,0 +1,42 @@
"""
All around managing users
"""
import os, json, hashlib, traceback, logging
from elasticsearch import NotFoundError, Elasticsearch # for normal read/write without vectors
from lib.models import User
from lib.elastictools import get_by_id, update_by_id, delete_by_id, wait_for_elasticsearch
elastic_url = os.getenv("ELASTIC_URI")
assert elastic_url
def hash_password(s: str) -> str:
return hashlib.md5(s.encode('utf-8')).hexdigest()
def create_user(email, password, role="user", verified=False):
user = User(meta={'id': email}, email=email, password_hash=hash_password(password + email), role=role)
user.creation_date = datetime.now()
user.isEmailVerified = verified
user.save()
return user
def create_default_users():
#create default users
client = Elasticsearch(elastic_url)
default_users = os.getenv("DEFAULT_USERS")
if default_users:
for (email, pwd, role) in json.loads(default_users):
if len(get_by_id(client, index="user", id_field_name="email", id_value=email)) == 0:
create_user(email, pwd, role=role, verified=True)

View File

@ -9,7 +9,6 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"></script>
<!-- <!--
load the following async:? load the following async:?
--> -->
@ -25,13 +24,10 @@
<script async="async" type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> </script> <script async="async" type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> </script>
<link async="async" rel='stylesheet' type='text/css' href='http://www.x3dom.org/download/x3dom.css'> <link async="async" rel='stylesheet' type='text/css' href='http://www.x3dom.org/download/x3dom.css'>
<script async="async" src="https://d3js.org/d3.v3.js"></script> <script async="async" src="https://d3js.org/d3.v3.js"></script>
<script async="async" src="jsnetworkx.js"></script> <script async="async" src="jsnetworkx.js"></script>
<script async="async" src="widget.js"></script> <script async="async" src="widget.js"></script>
</head> </head>
<body> <body>
@ -189,11 +185,11 @@
<a class="nav-link active" data-bs-toggle="tab" href="#home">Chat</a> <a class="nav-link active" data-bs-toggle="tab" href="#home">Chat</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#create_bot_tab">Create bot</a> <a id="tab2" class="nav-link disabled" data-bs-toggle="tab" href="#create_bot_tab">Create bot</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#tweak_bot_tab">Tweak bot</a> <a id="tab3" class="nav-link disabled" data-bs-toggle="tab" href="#tweak_bot_tab">Tweak bot</a>
</li> </li>
</ul> </ul>
@ -232,16 +228,16 @@
<div class="tab-pane container fade" id="create_bot_tab"> <div class="tab-pane container fade" id="create_bot_tab">
<div style="height: 10px !important;"></div> <div style="height: 10px !important;"></div>
<!--
<i>Creating a new bot requires an account and login via settings!</i> <i>Creating a new bot requires an account and login via settings!</i>
<br> <br>
<br> <br>
-->
<form> <form>
<label for="bot_name" class="form-label">Name:</label> <label for="bot_name" class="form-label">Name:</label>
<input type="bot_name" class="form-control" id="bot_name" placeholder="MyNewBot"> <input type="bot_name" class="form-control" id="bot_name" placeholder="The displayed name of the bot.">
<br> <br>
@ -255,7 +251,7 @@
<br> <br>
<label for="bot_description">Description:</label> <label for="bot_description">Description:</label>
<textarea id="bot_description" class="form-control" rows="8" name="text" placeholder="A bot that cares."></textarea> <textarea id="bot_description" class="form-control" rows="8" name="text" placeholder="A description of the bot and it's purpose."></textarea>
<br> <br>
@ -267,7 +263,7 @@
<br> <br>
<label for="bot_system_prompt">System prompt:</label> <label for="bot_system_prompt">System prompt:</label>
<textarea id="bot_system_prompt" class="form-control" rows="8" name="text" placeholder="Answer all questions short and sweet!"></textarea> <textarea id="bot_system_prompt" class="form-control" rows="8" name="text" placeholder="The prompt that defines the bot's main behaviour."></textarea>
<br> <br>
@ -320,17 +316,17 @@
<div class="tab-pane container fade" id="tweak_bot_tab"> <div class="tab-pane container fade" id="tweak_bot_tab">
<div style="height: 10px !important;"></div> <div style="height: 10px !important;"></div>
<!--
<i>Tweaking a new bot requires an account and login via settings!</i> <i>Tweaking a new bot requires an account and login via settings!</i>
<br> <br>
<br> <br>
-->
<form> <form>
<label for="bot_name" class="form-label">Name:</label> <label for="bot_name" class="form-label">Name:</label>
<input type="bot_name" class="form-control" id="bot_name" placeholder="MyNewBot"> <input type="bot_name" class="form-control" id="bot_name" placeholder="The displayed name of the bot.">
<br> <br>
@ -344,7 +340,7 @@
<br> <br>
<label for="bot_description">Description:</label> <label for="bot_description">Description:</label>
<textarea id="bot_description" class="form-control" rows="8" name="text" placeholder="A bot that cares."></textarea> <textarea id="bot_description" class="form-control" rows="8" name="text" placeholder="A description of the bot and it's purpose."></textarea>
<br> <br>
@ -356,7 +352,7 @@
<br> <br>
<label for="bot_system_prompt">System prompt:</label> <label for="bot_system_prompt">System prompt:</label>
<textarea id="bot_system_prompt" class="form-control" rows="8" name="text" placeholder="Answer all questions short and sweet!"></textarea> <textarea id="bot_system_prompt" class="form-control" rows="8" name="text" placeholder="The prompt that defines the bot's main behaviour."></textarea>
<br> <br>
@ -376,10 +372,12 @@
<div class="col"></div> <div class="col"></div>
<div class="col"></div> <div class="col"></div>
<div class="col"></div> <div class="col"></div>
<div class="col"></div>
<div class="col"> <div class="col">
<button id="change_bot_btn" disabled type="button" class="btn btn-primary text-white">Change bot</button> <button id="change_bot_btn" disabled type="button" class="btn btn-primary text-white">Change bot</button>
</div> </div>
<div class="col">
<button id="delete_bot_btn" disablxed type="button" class="btn btn-danger text-white">Delete bot</button>
</div>
</div> </div>
<br> <br>

View File

@ -316,26 +316,34 @@ window.onload = async ()=>{
bot_system_prompt.value = ""; bot_system_prompt.value = "";
} }
function set_ui_loggedin(b){ function set_ui_loggedin(b){
if(b){ if(b){
console.log("User logged in!");
//enable create bot button //enable create bot button
create_bot_btn.removeAttribute("disabled"); create_bot_btn.removeAttribute("disabled");
change_bot_btn.removeAttribute("disabled"); change_bot_btn.removeAttribute("disabled");
login_btn.style.display = "none"; login_btn.style.display = "none";
logout_btn.style.display = "block"; logout_btn.style.display = "block";
document.getElementById("tab2").classList.remove('disabled');
document.getElementById("tab3").classList.remove('disabled');
} }
else{ else{
console.log("User not logged in!");
//disable create bot button //disable create bot button
create_bot_btn.setAttribute("disabled", "disabled"); create_bot_btn.setAttribute("disabled", "disabled");
change_bot_btn.setAttribute("disabled", "disabled"); change_bot_btn.setAttribute("disabled", "disabled");
logout_btn.style.display = "none"; logout_btn.style.display = "none";
login_btn.style.display = "block"; login_btn.style.display = "block";
document.getElementById("tab2").classList.add('disabled');
document.getElementById("tab3").classList.add('disabled');
} }
} }
//init: are we logged in on start? //init: are we logged in on start?
let jwt = localStorage.getItem("jwt"); let jwt = localStorage.getItem("jwt");
if(jwt === null){ if(jwt === null){

View File

@ -64,7 +64,6 @@ services:
networks: networks:
- llm_network - llm_network
ollama: ollama:
container_name: ${APP_PREFIX}_ollama container_name: ${APP_PREFIX}_ollama
image: ollama/ollama:latest image: ollama/ollama:latest
@ -78,10 +77,9 @@ services:
- llm_network - llm_network
#command: "ollama pull llama2" #command: "ollama pull llama2"
ollama-webui: ollama-webui:
container_name: ${APP_PREFIX}_ollama-webui
image: ghcr.io/ollama-webui/ollama-webui:main image: ghcr.io/ollama-webui/ollama-webui:main
container_name: ollama-webui
volumes: volumes:
- ../ollama/ollama-webui:/app/backend/data - ../ollama/ollama-webui:/app/backend/data
depends_on: depends_on:
@ -112,15 +110,20 @@ services:
ports: ports:
- "5000:5000" - "5000:5000"
env_file: env_file:
- ../backend/backend.env
- .env - .env
environment:
- LOG_LEVEL=ERROR
- BOT_ROOT_PATH=/
- ELASTIC_URI=http://elasticsearch:9200
- OLLAMA_URI=http://ollama:11434
build: ../backend build: ../backend
networks: networks:
- llm_network - llm_network
depends_on: depends_on:
- elasticsearch - elasticsearch
- ollama - ollama
#- minio - minio
volumes: volumes:
esdata: esdata:

View File

@ -4,6 +4,16 @@
ollama recommends running the https://www.amd.com/en/support/linux-drivers: amdgpu version file missing:
/sys/module/amdgpu/version stat /sys/module/amdgpu/version: no such file or directory"
system_prompt = "Answer in the language of the question. Give a short answer. Answer all questions without reservations or advice." system_prompt = "Answer in the language of the question. Give a short answer. Answer all questions without reservations or advice."