new ui
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4m21s
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4m21s
This commit is contained in:
parent
f6f5138f6e
commit
95daf9ee74
19
.gitea/workflows/test.yml
Normal file
19
.gitea/workflows/test.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: Gitea Actions Demo
|
||||||
|
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Explore-Gitea-Actions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||||
|
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||||
|
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
|
||||||
|
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||||
|
- name: List files in the repository
|
||||||
|
run: |
|
||||||
|
ls ${{ gitea.workspace }}
|
||||||
|
- run: echo "🍏 This job's status is ${{ job.status }}."
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
backend/__pycache__/
|
||||||
|
|
||||||
|
|
28
README.md
28
README.md
@ -13,11 +13,33 @@ After deploy:
|
|||||||
* simple FE: http://localhost:5000/
|
* simple FE: http://localhost:5000/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Backend:
|
## Backend:
|
||||||
|
|
||||||
* http://localhost:5000/openapi/swagger
|
* http://localhost:5000/openapi/swagger
|
||||||
* http://localhost/backend/openapi/swagger
|
* http://localhost/backend/openapi/swagger
|
||||||
|
|
||||||
|
|
||||||
|
### Push image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo docker tag llm-python-backend nucberlin:5123/llm-python-backend
|
||||||
|
|
||||||
|
sudo docker push nucberlin:5123/llm-python-backend
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
## Ideas
|
||||||
|
|
||||||
|
### Knowledge graph creation
|
||||||
|
|
||||||
|
https://www.linkedin.com/posts/sivas-subramaniyan_microsoft-research-is-bullish-on-the-concept-activity-7194953376470638592-dQ-U/?utm_source=share&utm_medium=member_desktop
|
||||||
|
|
||||||
|
|
||||||
|
clean dangling images
|
||||||
|
|
||||||
|
sudo docker rmi $(sudo docker images -f "dangling=true" -q)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
395
backend/app.py
395
backend/app.py
@ -23,8 +23,11 @@ import tiktoken
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
from langchain.chains import RetrievalQA
|
from langchain.chains import RetrievalQA
|
||||||
|
|
||||||
|
|
||||||
from langchain_community.vectorstores.elasticsearch import ElasticsearchStore
|
from langchain_community.vectorstores.elasticsearch import ElasticsearchStore
|
||||||
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader
|
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader
|
||||||
|
from langchain_community.embeddings import OllamaEmbeddings
|
||||||
|
|
||||||
|
|
||||||
from langchain.callbacks.base import BaseCallbackHandler, BaseCallbackManager
|
from langchain.callbacks.base import BaseCallbackHandler, BaseCallbackManager
|
||||||
from langchain.prompts import PromptTemplate
|
from langchain.prompts import PromptTemplate
|
||||||
@ -101,50 +104,63 @@ app = OpenAPI(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def jwt_required(f):
|
def uses_jwt(required=True):
|
||||||
"""
|
"""
|
||||||
Wraps routes in a jwt-required logic and passes decoded jwt and user from elasticsearch to the route as keyword
|
Wraps routes in a jwt-required logic and passes decoded jwt and user from elasticsearch to the route as keyword
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(f)
|
def non_param_deco(f):
|
||||||
def decorated_route(*args, **kwargs):
|
@wraps(f)
|
||||||
token = None
|
def decorated_route(*args, **kwargs):
|
||||||
if "Authorization" in request.headers:
|
token = None
|
||||||
token = request.headers["Authorization"].split(" ")[1]
|
if "Authorization" in request.headers:
|
||||||
if not token:
|
token = request.headers["Authorization"].split(" ")[1]
|
||||||
return jsonify({
|
|
||||||
'status': 'error',
|
|
||||||
"message": "Authentication Token is missing!",
|
|
||||||
}), 401
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = pyjwt.decode(token, app.config["jwt_secret"], algorithms=["HS256"])
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'error',
|
|
||||||
"message": "JWT-decryption: " + str(e)
|
|
||||||
}), 401
|
|
||||||
|
|
||||||
try:
|
|
||||||
#user = get_by_id(client, index="user", id_field_name="email", id_value=data["email"])[0]
|
|
||||||
response = Search(using=client, index="user").filter("term", **{"email": data["email"]})[0:5].execute()
|
|
||||||
for hit in response:
|
|
||||||
user = hit
|
|
||||||
break
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'error',
|
|
||||||
"message": "Invalid Authentication token!"
|
|
||||||
}), 401
|
|
||||||
|
|
||||||
kwargs["decoded_jwt"] = data
|
|
||||||
kwargs["user"] = user
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
|
|
||||||
return decorated_route
|
|
||||||
|
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
if required:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
"message": "Authentication Token is missing!",
|
||||||
|
}), 401
|
||||||
|
|
||||||
|
else:
|
||||||
|
kwargs["decoded_jwt"] = {}
|
||||||
|
kwargs["user"] = None
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = pyjwt.decode(token, app.config["jwt_secret"], algorithms=["HS256"])
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
"message": "JWT-decryption: " + str(e)
|
||||||
|
}), 401
|
||||||
|
|
||||||
|
try:
|
||||||
|
#user = get_by_id(client, index="user", id_field_name="email", id_value=data["email"])[0]
|
||||||
|
#response = Search(using=client, index="user").filter("term", **{"email": data["email"]})[0:5].execute()
|
||||||
|
#response = Search(index="user").filter("term", **{"email": data["email"]})[0:5].execute()
|
||||||
|
response = User.search().filter("term", **{"email": data["email"]})[0:5].execute()
|
||||||
|
for hit in response:
|
||||||
|
user = hit
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
"message": "Invalid Authentication token!"
|
||||||
|
}), 401
|
||||||
|
|
||||||
|
kwargs["decoded_jwt"] = data
|
||||||
|
kwargs["user"] = user
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
return decorated_route
|
||||||
|
|
||||||
|
return non_param_deco
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -200,6 +216,20 @@ for env_key, conf_key in env_to_conf.items():
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def ask_bot(question, bot_id):
|
||||||
|
bot = Chatbot.get(id=bot_id)
|
||||||
|
llm = Ollama(
|
||||||
|
model=bot.llm_model,
|
||||||
|
base_url="http://ollama:11434"
|
||||||
|
)
|
||||||
|
query = bot.system_prompt + " " + question
|
||||||
|
for chunk in llm.stream(query):
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#from flask_cors import CORS #falls cross-orgin verwendet werden soll
|
#from flask_cors import CORS #falls cross-orgin verwendet werden soll
|
||||||
#CORS(app)
|
#CORS(app)
|
||||||
|
|
||||||
@ -220,36 +250,35 @@ def sockcon(data):
|
|||||||
@socket.on('client message')
|
@socket.on('client message')
|
||||||
def handle_message(message):
|
def handle_message(message):
|
||||||
|
|
||||||
|
#room = message["room"]
|
||||||
|
#stream_key = "chatbot_stream"
|
||||||
|
|
||||||
|
#llm = Ollama(
|
||||||
|
# model="llama3",
|
||||||
|
# base_url="http://ollama:11434"
|
||||||
|
#)
|
||||||
|
|
||||||
|
#system_prompt = ""
|
||||||
|
|
||||||
|
#query = system_prompt + " " + message["data"]
|
||||||
|
#print(message["data"])
|
||||||
|
|
||||||
|
#for chunks in llm.stream(query):
|
||||||
|
# socket.emit('backend token', {'data': chunks, "done": False}, to=room)
|
||||||
|
|
||||||
|
#socket.emit('backend token', {'done': True}, to=room)
|
||||||
|
|
||||||
room = message["room"]
|
room = message["room"]
|
||||||
stream_key = "chatbot_stream"
|
question = message["question"]
|
||||||
|
bot_id = message["bot_id"]
|
||||||
llm = Ollama(
|
|
||||||
model="llama3",
|
|
||||||
base_url="http://ollama:11434"
|
|
||||||
)
|
|
||||||
|
|
||||||
#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."
|
|
||||||
#system_prompt = "Always answer in English and give a short answer."
|
|
||||||
#system_prompt = "Always answer in English and give a short answer. If the answer is a list give it only as a JSON array."
|
|
||||||
#system_prompt = "Write the answer as Prolog assertions."
|
|
||||||
#system_prompt = "Write the answer in Japanese."
|
|
||||||
#system_prompt = "Write the answer in Japanese."
|
|
||||||
system_prompt = ""
|
|
||||||
#Write the answer as JSON only.
|
|
||||||
#If the answer is a geographic position return a JSON-object with the longitude and latitude as attributes.
|
|
||||||
|
|
||||||
|
|
||||||
query = system_prompt + " " + message["data"]
|
|
||||||
print(message["data"])
|
|
||||||
|
|
||||||
for chunks in llm.stream(query):
|
|
||||||
socket.emit('backend token', {'data': chunks, "done": False}, to=room)
|
|
||||||
|
|
||||||
|
for chunk in ask_bot(question, bot_id):
|
||||||
|
socket.emit('backend token', {'data': chunk, "done": False}, to=room)
|
||||||
socket.emit('backend token', {'done': True}, to=room)
|
socket.emit('backend token', {'done': True}, to=room)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_embedding():
|
def create_embedding():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -262,11 +291,16 @@ def hash_password(s: str) -> str:
|
|||||||
|
|
||||||
jwt_tag = Tag(name='JWT', description='Requires a valid JSON Web Token')
|
jwt_tag = Tag(name='JWT', description='Requires a valid JSON Web Token')
|
||||||
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')
|
||||||
|
debug_tag = Tag(name='Debug', description='Debug')
|
||||||
|
|
||||||
|
|
||||||
|
bot_tag = Tag(name='Bot', description='Bot')
|
||||||
|
|
||||||
|
|
||||||
#==============Routes===============
|
#==============Routes===============
|
||||||
|
|
||||||
class LoginRequest(BaseModel):
|
class LoginRequest(BaseModel):
|
||||||
email: str = Field(None, description='A short text by the user explaining the rating.')
|
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.')
|
||||||
|
|
||||||
|
|
||||||
@ -275,6 +309,15 @@ def login(form: LoginRequest):
|
|||||||
"""
|
"""
|
||||||
Get your JWT to verify access rights
|
Get your JWT to verify access rights
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if form.email is None or form.password is None:
|
||||||
|
msg = "Invalid password!"
|
||||||
|
app.logger.error(msg)
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': msg
|
||||||
|
}), 400
|
||||||
|
|
||||||
client = Elasticsearch(app.config['elastic_uri'])
|
client = Elasticsearch(app.config['elastic_uri'])
|
||||||
match get_by_id(client, index="user", id_field_name="email", id_value=form.email):
|
match get_by_id(client, index="user", id_field_name="email", id_value=form.email):
|
||||||
case []:
|
case []:
|
||||||
@ -286,8 +329,13 @@ def login(form: LoginRequest):
|
|||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
case [user]:
|
case [user]:
|
||||||
if user["password_hash"] == hash_password(form.password):
|
if user["password_hash"] == hash_password(form.password + form.email):
|
||||||
return pyjwt.encode({"email": form.email}, app.config['jwt_secret'], algorithm="HS256")
|
token = pyjwt.encode({"email": form.email}, app.config['jwt_secret'], algorithm="HS256")
|
||||||
|
#app.logger.info(token)
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'jwt': token
|
||||||
|
})
|
||||||
else:
|
else:
|
||||||
msg = "Invalid password!"
|
msg = "Invalid password!"
|
||||||
app.logger.error(msg)
|
app.logger.error(msg)
|
||||||
@ -297,44 +345,213 @@ def login(form: LoginRequest):
|
|||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
|
|
||||||
|
#-----bot routes------
|
||||||
|
|
||||||
class IndexSchemaRequest(BaseModel):
|
class GetBotRequest(BaseModel):
|
||||||
#end: datetime = Field("2100-01-31T16:47+00:00", description="""The interval end datetime in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format""")
|
id: str = Field(None, description="The bot's id")
|
||||||
pass
|
|
||||||
|
|
||||||
|
@app.get('/bot', summary="", tags=[bot_tag], security=security)
|
||||||
|
@uses_jwt(required=False)
|
||||||
|
def get_bots(query: GetBotRequest, decoded_jwt, user):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.get('/bot', summary="", tags=[jwt_tag], security=security)
|
|
||||||
@jwt_required
|
|
||||||
def get_all_bots(decoded_jwt, user):
|
|
||||||
"""
|
"""
|
||||||
List all bots for a user identified by the JWT.
|
List all bots or one by id
|
||||||
"""
|
"""
|
||||||
#client = Elasticsearch(app.config['elastic_uri'])
|
match query.id:
|
||||||
#bots = get_by_id(client, index="chatbot", id_field_name="createdBy", id_value=nextsearch_user.meta.id)
|
case None:
|
||||||
#return jsonify(bots)
|
match user:
|
||||||
return jsonify([])
|
case None:
|
||||||
|
#get all public bots
|
||||||
|
ls = []
|
||||||
|
for hit in Chatbot.search()[0:10000].execute():
|
||||||
|
d = hit.to_dict()
|
||||||
|
if d["visibility"] == "public":
|
||||||
|
d["id"] = hit.meta.id
|
||||||
|
ls.append(d)
|
||||||
|
|
||||||
|
return jsonify(ls)
|
||||||
|
|
||||||
|
case _:
|
||||||
|
#get all user bots
|
||||||
|
ls = []
|
||||||
|
for hit in Chatbot.search()[0:10000].execute():
|
||||||
|
d = hit.to_dict()
|
||||||
|
if "creator_id" in d:
|
||||||
|
if user.meta.id == d["creator_id"]:
|
||||||
|
d["id"] = hit.meta.id
|
||||||
|
ls.append(d)
|
||||||
|
|
||||||
|
return jsonify(ls)
|
||||||
|
|
||||||
|
case some_id:
|
||||||
|
match user:
|
||||||
|
case None:
|
||||||
|
bot = Chatbot.get(id=query.id)
|
||||||
|
if bot.visibility == "public":
|
||||||
|
d = bot.to_dict()
|
||||||
|
d["id"] = bot.meta.id
|
||||||
|
return jsonify(d)
|
||||||
|
else:
|
||||||
|
return jsonify(None)
|
||||||
|
case _:
|
||||||
|
bot = Chatbot.get(id=query.id)
|
||||||
|
d = bot.to_dict()
|
||||||
|
d["id"] = bot.meta.id
|
||||||
|
return jsonify(d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.post('/bot', summary="", tags=[jwt_tag, not_implemented_tag], security=security)
|
|
||||||
@jwt_required
|
|
||||||
def create_bot(query: IndexSchemaRequest):
|
|
||||||
|
class CreateBotRequest(BaseModel):
|
||||||
|
name: str = Field(None, description="The bot's name")
|
||||||
|
visibility: str = Field('private', description="The bot's visibility to other users ('private', 'public')")
|
||||||
|
description: str = Field('', description="The bot's description of purpose and being")
|
||||||
|
system_prompt: str = Field('', description="The bot's defining system prompt")
|
||||||
|
llm_model: str = Field("llama3", description="The bot's used LLM")
|
||||||
|
|
||||||
|
#status = Keyword()
|
||||||
|
#temperature = Float()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/bot', summary="", tags=[bot_tag], security=security)
|
||||||
|
@uses_jwt()
|
||||||
|
def create_bot(form: CreateBotRequest, decoded_jwt, user):
|
||||||
"""
|
"""
|
||||||
Creates a chatbot for the JWT associated user.
|
Creates a chatbot for the JWT associated user.
|
||||||
"""
|
"""
|
||||||
|
bot = Chatbot()
|
||||||
|
bot.name = form.name
|
||||||
|
bot.visibility = form.visibility
|
||||||
|
bot.description = form.description
|
||||||
|
bot.system_prompt = form.system_prompt
|
||||||
|
bot.llm_model = form.llm_model
|
||||||
|
|
||||||
|
#add meta data
|
||||||
|
bot.creation_date = datetime.now()
|
||||||
|
bot.creator_id = user.meta.id
|
||||||
|
bot.save()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"bot_id": bot.meta.id
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteBotRequest(BaseModel):
|
||||||
|
id: str = Field(None, description="The bot's id")
|
||||||
|
|
||||||
|
@app.delete('/bot', summary="", tags=[bot_tag], security=security)
|
||||||
|
@uses_jwt()
|
||||||
|
def delete_bot(form: DeleteBotRequest, decoded_jwt, user):
|
||||||
|
"""
|
||||||
|
Deletes a chatbot via it's id
|
||||||
|
"""
|
||||||
|
bot = Chatbot.get(id=form.id)
|
||||||
|
bot.delete()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateBotRequest(BaseModel):
|
||||||
|
id: str = Field(None, description="The bot's id")
|
||||||
|
|
||||||
|
@app.put('/bot', summary="", tags=[bot_tag], security=security)
|
||||||
|
@uses_jwt()
|
||||||
|
def update_bot(form: UpdateBotRequest, decoded_jwt, user):
|
||||||
|
"""
|
||||||
|
Changes a chatbot
|
||||||
|
"""
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class AskBotRequest(BaseModel):
|
||||||
|
bot_id: str = Field(None, description="The bot's id")
|
||||||
|
question: str = Field(None, description="The question the bot should answer")
|
||||||
|
|
||||||
|
@app.get('/bot/ask', summary="", tags=[bot_tag], security=security)
|
||||||
|
@uses_jwt()
|
||||||
|
def query_bot(query: AskBotRequest, decoded_jwt, user):
|
||||||
|
"""
|
||||||
|
Asks a chatbot
|
||||||
|
"""
|
||||||
|
r = ""
|
||||||
|
for chunk in ask_bot(question=query.question, bot_id=query.bot_id):
|
||||||
|
r += chunk
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"answer": r
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------Embedding----------------------
|
||||||
|
|
||||||
|
class TrainTextRequest(BaseModel):
|
||||||
|
chatbot_id: str = Field(None, description="The bot's id")
|
||||||
|
text: str = Field(None, description="Some text")
|
||||||
|
|
||||||
|
#TODO: needs to be reimplemented with another mechanism like celeery to manage longer running tasks and give feedback to frontend
|
||||||
|
|
||||||
|
@app.post('/bot/train', summary="", tags=[jwt_tag], security=security)
|
||||||
|
@uses_jwt()
|
||||||
|
def upload(form: TrainTextRequest, decoded_jwt, nextsearch_user):
|
||||||
|
"""
|
||||||
|
Caution: Long running request!
|
||||||
|
"""
|
||||||
|
chatbot_id = form.chatbot_id
|
||||||
|
text = form.text
|
||||||
|
|
||||||
|
# validate body
|
||||||
|
if not chatbot_id:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'chatbotId is required'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
if not text:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'No data source found'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ESDocument = namedtuple('Document', ['page_content', 'metadata'])
|
||||||
|
|
||||||
|
txt_id = hashlib.md5(text.encode()).hexdigest()
|
||||||
|
|
||||||
|
#train with given text
|
||||||
|
ls = []
|
||||||
|
for i, s in enumerate(RecursiveCharacterTextSplitter(chunk_size=1536, chunk_overlap=200, length_function=len).split_text(text)):
|
||||||
|
ls.append(ESDocument(
|
||||||
|
page_content=s,
|
||||||
|
metadata={
|
||||||
|
"chatbot_id": chatbot_id,
|
||||||
|
"text_id": txt_id
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def determine_index(chatbot_id: str) -> str:
|
||||||
|
index_prefix = "chatbot"
|
||||||
|
return f"{index_prefix}_{chatbot_id.lower()}"
|
||||||
|
|
||||||
|
|
||||||
|
#index = determine_index(chatbot_id)
|
||||||
|
|
||||||
|
embedding = OllamaEmbeddings()
|
||||||
|
|
||||||
|
ElasticsearchStore.from_documents(ls, embedding, index_name="embed_text", es_url=app.config['elastic_uri'])
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "success"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
#======== DEBUG routes ============
|
#======== DEBUG routes ============
|
||||||
|
|
||||||
@app.get('/bot/debug/schema', summary="", tags=[])
|
@app.get('/debug/schema', summary="", tags=[debug_tag])
|
||||||
def get_schema(query: IndexSchemaRequest):
|
def get_schema():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -393,8 +610,8 @@ def create_default_users():
|
|||||||
if default_users:
|
if default_users:
|
||||||
for (email, pwd, role) in json.loads(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:
|
if len(get_by_id(client, index="user", id_field_name="email", id_value=email)) == 0:
|
||||||
user = User(email=email, password_hash=hash_password(pwd), role=role)
|
user = User(email=email, password_hash=hash_password(pwd + email), role=role)
|
||||||
#user.published_from = datetime.now()
|
user.creation_date = datetime.now()
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
ELASTIC_URI=http://elasticsearch:9200
|
ELASTIC_URI=http://elasticsearch:9200
|
||||||
|
|
||||||
DEFAULT_USERS=[["user@gmail.com", "1234", "user"], ["admin@gmail.com", "1234", "admin"]]
|
DEFAULT_USERS=[["tobias_weise@gmx.de", "myEpicPwd123", "admin"]]
|
||||||
|
|
||||||
# DEFAULT_USERS is list of lists, each nested list respectively contains email, password and role
|
# DEFAULT_USERS is list of lists, each nested list respectively contains email, password and role
|
||||||
# e.g. [["user@gmail.com", "1234", "user"], ["admin@gmail.com", "1234", "admin"]]
|
# e.g. [["user@gmail.com", "1234", "user"], ["admin@gmail.com", "1234", "admin"]]
|
||||||
|
@ -5,101 +5,276 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script src="viz.js"></script>
|
<script src="viz.js"></script>
|
||||||
<script src="viz_widget.js"></script>
|
<script src="viz_widget.js"></script>
|
||||||
|
<script src="tabs.js"></script>
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="w3.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://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<!--
|
<div class="container-fluid p-3 bg-primary text-white text-center">
|
||||||
<dot-graph layout="fdp" style="width:800px; height:600px; box-shadow: 10px 5px 5px black;">
|
<h1>Ollama Chatbot</h1>
|
||||||
|
<p>Create and talk to chatbots!</p>
|
||||||
digraph G {
|
</div>
|
||||||
|
|
||||||
graph [
|
|
||||||
splines=spline
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
node [shape=rect];
|
|
||||||
A->B
|
|
||||||
A->C
|
|
||||||
A->D
|
|
||||||
C->D
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</dot-graph>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<div class="w3-container">
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
<div class="w3-container w3-teal">
|
<!-- Offcanvas Sidebar -->
|
||||||
<h1>Ollama Chatbot</h1>
|
<div class="offcanvas offcanvas-start text-bg-dark" id="demo">
|
||||||
</div>
|
<div class="offcanvas-header">
|
||||||
<br>
|
<h1 class="offcanvas-title">Settings</h1>
|
||||||
|
<button type="button" class="btn-close btn-close-white text-reset" data-bs-dismiss="offcanvas"></button>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-body">
|
||||||
|
|
||||||
<details>
|
<button id="login_btn" type="button" class="btn btn-primary text-white">Login</button>
|
||||||
<summary class="w3-button w3-round w3-teal">Settings...</summary>
|
<button id="logout_btn" type="button" class="btn btn-danger text-white">Logout</button>
|
||||||
|
<br>
|
||||||
|
|
||||||
<div class="w3-panel w3-pale-green">
|
<label for="system_prompt">System prompt:</label>
|
||||||
<h2>System prompt:</h2>
|
<textarea id="system_prompt" class="form-control" rows="8" name="text"></textarea>
|
||||||
<textarea class="w3-input w3-border" type="text" style="width: 98%; height: 50px;" id="system_prompt" >Write the answer as JSON only.</textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</details>
|
<br>
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div id="scroll_div" class="w3-container" style="overflow:scroll; height: 400px;">
|
<label for="bots">Choose a bot:</label>
|
||||||
<table id="log" class="w3-table-all" style="width: 100%;"></table>
|
<select name="bots" id="bot_select" class="form-select"></select>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<label for="views">Choose a view:</label>
|
||||||
|
<select name="views" id="view_select" class="form-select">
|
||||||
|
<option value="md">Markdown</option>
|
||||||
|
<option value="dot">Dot-Lang</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<textarea class="w3-input w3-border" style="height: 50px;" id="user_input"></textarea>
|
<div style="height: 5px !important;"></div>
|
||||||
<button class="w3-button w3-teal" id="submit_btn">Send ❯</button>
|
|
||||||
|
<!-- Nav tabs -->
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" data-bs-toggle="tab" href="#home">Chat</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" data-bs-toggle="tab" href="#create_bot_tab">Create bot</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Tab panes -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane container active" id="home">
|
||||||
|
|
||||||
|
<div style="height: 10px !important;"></div>
|
||||||
|
|
||||||
|
<div id="scroll_div" class="card container" style="overflow:scroll; height: 400px;">
|
||||||
|
<table id="log" class="table" style="width: 100%;"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">@bot</span>
|
||||||
|
<input id="user_input" type="text" class="form-control" placeholder="What is...">
|
||||||
|
<button id="submit_btn" class="btn btn-success" type="submit">Send</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Button to open the offcanvas sidebar -->
|
||||||
|
<button class="btn btn-light" type="button" data-bs-toggle="offcanvas" data-bs-target="#demo">
|
||||||
|
Settings...
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane container fade" id="create_bot_tab">
|
||||||
|
|
||||||
|
<div style="height: 10px !important;"></div>
|
||||||
|
|
||||||
|
<i>Creating a new bot requires an account and login via settings!</i>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<label for="bot_name" class="form-label">Name:</label>
|
||||||
|
<input type="bot_name" class="form-control" id="bot_name" placeholder="MyNewBot">
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<label for="bot_visibility">Visibility:</label>
|
||||||
|
<select name="bot_visibility" id="bot_visibility_select" class="form-select">
|
||||||
|
<option value="public">Public to All</option>
|
||||||
|
<option value="private">Private to User</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<label for="bot_description">Description:</label>
|
||||||
|
<textarea id="bot_description" class="form-control" rows="8" name="text" placeholder="A bot that cares."></textarea>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<label for="bot_llm">Language model:</label>
|
||||||
|
<select name="bot_llm" id="bot_llm_select" class="form-select">
|
||||||
|
<option value="llama3">Llama3</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col">
|
||||||
|
<button id="create_bot_btn" disabled "type="button" class="btn btn-primary text-white">Create bot</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- alerts -->
|
||||||
|
|
||||||
|
<div id="alert_spawn"></div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div id="alert_bot_created" style="display: none;" class="alert alert-success alert-dismissible fade show">
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
<strong>Success!</strong> Bot created!
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="alert_not_bot_created" style="display: none;" class="alert alert-danger alert-dismissible fade show">
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
<strong>Couldn't create bot!</strong> Something killed that bot!
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<br>
|
|
||||||
<div class="w3-container w3-teal">
|
|
||||||
<p>tobiasweise.dev</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" charset="utf-8">
|
<footer>
|
||||||
window.onload = ()=>{
|
<div class="container-fluid p-3 bg-primary text-white mt-5">
|
||||||
let tA = document.getElementById("user_input");
|
|
||||||
let log = document.getElementById("log");
|
|
||||||
let btn = document.getElementById("submit_btn");
|
|
||||||
|
|
||||||
function log_msg(nick, msg){
|
<div class="row">
|
||||||
console.log(nick + ": " + msg);
|
<div class="col-sm-4">
|
||||||
log.innerHTML += "<tr><td><b>" + nick + "</b>:</td><td>" + msg + "</td></tr>";
|
<h3>A simple UI</h3>
|
||||||
|
<p>This is just a simple frontend with basic functionality hosted on the REST-backend.</p>
|
||||||
|
<p>A standalone frontend written in Vue.js is in development</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<h3>Tools used</h3>
|
||||||
|
<p>❯ Ollama, Llama3</p>
|
||||||
|
<p>❯ Elasticsearch, LangChain</p>
|
||||||
|
<p>❯ Flask, OpenAPI</p>
|
||||||
|
<p>❯ Bootstrap 5</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<h3>Who? Why?</h3>
|
||||||
|
<p>The guy on this site: <a class="text-white" href="https://tobiasweise.dev">tobiasweise.dev</a></p>
|
||||||
|
<p>For fun and learning...</p>
|
||||||
|
<p>...and maybe getting a job that employs the used skills.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
//idea: generate proxy opject via openapi.json api(url).login_now()
|
||||||
|
|
||||||
|
async function login(email, pwd){
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("email", email);
|
||||||
|
formData.append("password", pwd);
|
||||||
|
const response = await fetch("/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'accept': '*/*'
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_bots(jwt){
|
||||||
|
if(jwt){
|
||||||
|
const response = await fetch("/bot", {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
'accept': '*/*',
|
||||||
|
'Authorization': 'Bearer ' + jwt
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
const response = await fetch("/bot", {
|
||||||
function scroll_down(){
|
method: "GET",
|
||||||
let div = document.getElementById("scroll_div");
|
headers: {
|
||||||
div.scrollTop = div.scrollHeight;
|
'accept': '*/*'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log_msg("Bot", "Ask a question!");
|
|
||||||
|
|
||||||
|
|
||||||
|
async function create_bot(jwt, name, visibility, description, llm, sys_prompt){
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("name", name);
|
||||||
|
formData.append("visibility", visibility);
|
||||||
|
formData.append("description", description);
|
||||||
|
formData.append("llm_model", llm);
|
||||||
|
formData.append("system_prompt", sys_prompt);
|
||||||
|
|
||||||
|
const response = await fetch("/bot", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'accept': '*/*',
|
||||||
|
'Authorization': 'Bearer ' + jwt
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ask_question(s){
|
||||||
const socket = io();
|
const socket = io();
|
||||||
|
|
||||||
let room = null;
|
let room = null;
|
||||||
socket.on('backend response', function(data) {
|
socket.on('backend response', data =>{
|
||||||
console.log(data);
|
console.log(data);
|
||||||
if(data.room) room = data.room;
|
if(data.room) room = data.room;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let answer_count = 0;
|
let answer_count = 0;
|
||||||
let acc_text = "";
|
let acc_text = "";
|
||||||
let first_token = true;
|
let first_token = true;
|
||||||
|
|
||||||
socket.on('backend token', function(obj) {
|
socket.on('backend token', obj =>{
|
||||||
console.log(obj);
|
console.log(obj);
|
||||||
if(first_token){
|
if(first_token){
|
||||||
let id = answer_count;
|
let id = answer_count;
|
||||||
@ -110,15 +285,306 @@
|
|||||||
acc_text += "" + obj.data;
|
acc_text += "" + obj.data;
|
||||||
first_token = false;
|
first_token = false;
|
||||||
document.getElementById(answer_count).innerHTML += obj.data;
|
document.getElementById(answer_count).innerHTML += obj.data;
|
||||||
|
|
||||||
scroll_down();
|
scroll_down();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
//log_msg("Bot", acc_text);
|
//log_msg("Bot", acc_text);
|
||||||
|
|
||||||
|
let final_answer = document.getElementById(answer_count).textContent;
|
||||||
|
|
||||||
|
//alert(final_answer);
|
||||||
|
|
||||||
|
final_answer = final_answer.replace("```", "").replace("```", "");
|
||||||
|
|
||||||
|
switch(view_select.value){
|
||||||
|
|
||||||
|
case "md":
|
||||||
|
//document.getElementById(answer_count).innerHTML += obj.data;
|
||||||
|
document.getElementById(answer_count).innerHTML = marked.parse(final_answer);
|
||||||
|
break;
|
||||||
|
case "dot":
|
||||||
|
//let layout = "fdp";
|
||||||
|
let layout = "dot";
|
||||||
|
document.getElementById(answer_count).innerHTML = `<dot-graph layout="${layout}" style="width:100%; height:100%;">${final_answer}</dot-graph>`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//document.getElementById(answer_count).innerHTML += obj.data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
acc_text = "";
|
acc_text = "";
|
||||||
first_token = true;
|
first_token = true;
|
||||||
answer_count += 1;
|
answer_count += 1;
|
||||||
|
scroll_down();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//send the request
|
||||||
|
let tA2 = document.getElementById("system_prompt");
|
||||||
|
socket.emit('client message', {
|
||||||
|
question: tA2.value + " " + s,
|
||||||
|
bot_id: bot_select.value,
|
||||||
|
room: room
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
next(){
|
||||||
|
return {
|
||||||
|
done: false,
|
||||||
|
value: new Promise((resolve,reject)=>{
|
||||||
|
resolve(1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = async ()=>{
|
||||||
|
document.documentElement.style.setProperty("--bs-primary-rgb", "45, 124, 172");
|
||||||
|
|
||||||
|
//chat
|
||||||
|
let tA = document.getElementById("user_input");
|
||||||
|
let log = document.getElementById("log");
|
||||||
|
let btn = document.getElementById("submit_btn");
|
||||||
|
let scroll_div = document.getElementById("scroll_div");
|
||||||
|
|
||||||
|
//settings
|
||||||
|
let bot_select = document.getElementById("bot_select");
|
||||||
|
let view_select = document.getElementById("view_select");
|
||||||
|
let login_btn = document.getElementById("login_btn");
|
||||||
|
let logout_btn = document.getElementById("logout_btn");
|
||||||
|
|
||||||
|
//create bot form
|
||||||
|
let create_bot_btn = document.getElementById("create_bot_btn");
|
||||||
|
let bot_name = document.getElementById("bot_name");
|
||||||
|
let bot_visibility_select = document.getElementById("bot_visibility_select");
|
||||||
|
let bot_description = document.getElementById("bot_description");
|
||||||
|
let bot_llm_select = document.getElementById("bot_llm_select");
|
||||||
|
let bot_system_prompt = document.getElementById("bot_system_prompt");
|
||||||
|
|
||||||
|
let alert_spawn = document.getElementById("alert_spawn");
|
||||||
|
|
||||||
|
|
||||||
|
function log_msg(nick, msg){
|
||||||
|
console.log(nick + ": " + msg);
|
||||||
|
log.innerHTML += "<tr><td><b>" + nick + "</b>:</td><td>" + msg + "</td></tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
function scroll_down(){
|
||||||
|
scroll_div.scrollTop = scroll_div.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_bot_name(){
|
||||||
|
let i = bot_select.selectedIndex;
|
||||||
|
if(i===-1) return "Bot";
|
||||||
|
return bot_select.options[i].text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_bot_list(ls){
|
||||||
|
bot_select.innerHTML = ls.map(bot => `<option value="${bot.id}">${bot.name}</option>`).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clean_bot_create_form(){
|
||||||
|
bot_name.value = "";
|
||||||
|
bot_description.value = "";
|
||||||
|
bot_system_prompt.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function alert_bot_creation(success){
|
||||||
|
if(success){
|
||||||
|
alert_spawn.innerHTML = `
|
||||||
|
<div class="alert alert-success alert-dismissible fade show">
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
<strong>Success!</strong> Bot created!
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
alert_spawn.innerHTML = `
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show">
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
<strong>Couldn't create bot!</strong> Something killed that bot!
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function set_ui_loggedin(b){
|
||||||
|
if(b){
|
||||||
|
//enable create bot button
|
||||||
|
create_bot_btn.removeAttribute("disabled");
|
||||||
|
login_btn.style.display = "none";
|
||||||
|
logout_btn.style.display = "block";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//disable create bot button
|
||||||
|
create_bot_btn.setAttribute("disabled", "disabled");
|
||||||
|
logout_btn.style.display = "none";
|
||||||
|
login_btn.style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//init: are we logged in on start?
|
||||||
|
let jwt = localStorage.getItem("jwt");
|
||||||
|
if(jwt === null){
|
||||||
|
let ls = await get_bots();
|
||||||
|
if(ls.length === 0){
|
||||||
|
console.error("No bots found!");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
set_bot_list(ls);
|
||||||
|
}
|
||||||
|
set_ui_loggedin(false);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
let ls = await get_bots(jwt);
|
||||||
|
if(ls.length === 0){
|
||||||
|
console.error("No bots found!");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
set_bot_list(ls);
|
||||||
|
}
|
||||||
|
set_ui_loggedin(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//for await (let x of new_async_gen()){
|
||||||
|
// alert(x);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
//init buttons
|
||||||
|
create_bot_btn.onclick = async ()=>{
|
||||||
|
let jwt = localStorage.getItem("jwt");
|
||||||
|
if(jwt){
|
||||||
|
let name = bot_name.value;
|
||||||
|
let visibility = bot_visibility_select.value;
|
||||||
|
let description = bot_description.value;
|
||||||
|
let llm = bot_llm_select.value;
|
||||||
|
let sys_prompt = bot_system_prompt.value;
|
||||||
|
|
||||||
|
try{
|
||||||
|
await create_bot(jwt, name, visibility, description, llm, sys_prompt);
|
||||||
|
alert_bot_creation(true);
|
||||||
|
clean_bot_create_form();
|
||||||
|
}
|
||||||
|
catch(err){
|
||||||
|
console.error(err);
|
||||||
|
console.error("Couldn't create bot!");
|
||||||
|
alert_bot_creation(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
login_btn.onclick = async ()=>{
|
||||||
|
let nick = prompt("Please enter your email");
|
||||||
|
let pwd = prompt("Please enter your password");
|
||||||
|
|
||||||
|
try{
|
||||||
|
let{jwt} = await login(nick, pwd);
|
||||||
|
|
||||||
|
if(!jwt) throw Error("No JWT!");
|
||||||
|
|
||||||
|
localStorage.setItem("jwt", jwt);
|
||||||
|
|
||||||
|
set_ui_loggedin(true);
|
||||||
|
|
||||||
|
let ls = await get_bots(jwt);
|
||||||
|
if(ls.length === 0){
|
||||||
|
console.error("No bots found!");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
set_bot_list(ls);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.error("Login failed!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
logout_btn.onclick = async ()=>{
|
||||||
|
localStorage.removeItem("jwt");
|
||||||
|
|
||||||
|
set_ui_loggedin(false);
|
||||||
|
|
||||||
|
let ls = await get_bots();
|
||||||
|
if(ls.length === 0){
|
||||||
|
console.error("No bots found!");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
set_bot_list(ls);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//init chat
|
||||||
|
log_msg(get_bot_name(), "Ask a question!");
|
||||||
|
|
||||||
|
const socket = io();
|
||||||
|
|
||||||
|
let room = null;
|
||||||
|
socket.on('backend response', data =>{
|
||||||
|
console.log(data);
|
||||||
|
if(data.room) room = data.room;
|
||||||
|
});
|
||||||
|
|
||||||
|
let answer_count = 0;
|
||||||
|
let acc_text = "";
|
||||||
|
let first_token = true;
|
||||||
|
|
||||||
|
socket.on('backend token', obj =>{
|
||||||
|
console.log(obj);
|
||||||
|
if(first_token){
|
||||||
|
let id = answer_count;
|
||||||
|
log.innerHTML += `<tr><td><b>${get_bot_name()}</b>:</td><td id="${id}"></td></tr>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!obj.done){
|
||||||
|
acc_text += "" + obj.data;
|
||||||
|
first_token = false;
|
||||||
|
document.getElementById(answer_count).innerHTML += obj.data;
|
||||||
|
scroll_down();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
|
||||||
|
let final_answer = acc_text;
|
||||||
|
console.log(final_answer);
|
||||||
|
|
||||||
|
|
||||||
|
switch(view_select.value){
|
||||||
|
|
||||||
|
case "md":
|
||||||
|
//document.getElementById(answer_count).innerHTML += obj.data;
|
||||||
|
document.getElementById(answer_count).innerHTML = marked.parse(final_answer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "dot":
|
||||||
|
final_answer = final_answer.replace("```dot", "").replace("```", "");
|
||||||
|
//let layout = "fdp";
|
||||||
|
let layout = "dot";
|
||||||
|
document.getElementById(answer_count).innerHTML = `<dot-graph layout="${layout}" style="width:100%; height:100%;">${final_answer}</dot-graph>`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//document.getElementById(answer_count).innerHTML += obj.data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc_text = "";
|
||||||
|
first_token = true;
|
||||||
|
answer_count += 1;
|
||||||
scroll_down();
|
scroll_down();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -130,7 +596,11 @@
|
|||||||
log_msg('User', s);
|
log_msg('User', s);
|
||||||
|
|
||||||
let tA2 = document.getElementById("system_prompt");
|
let tA2 = document.getElementById("system_prompt");
|
||||||
socket.emit('client message', {data: tA2.value + " " + s, room: room});
|
socket.emit('client message', {
|
||||||
|
question: tA2.value + " " + s,
|
||||||
|
bot_id: bot_select.value,
|
||||||
|
room: room
|
||||||
|
});
|
||||||
}
|
}
|
||||||
scroll_down();
|
scroll_down();
|
||||||
};
|
};
|
||||||
|
@ -5,10 +5,12 @@ from elasticsearch_dsl import Document, InnerDoc, Nested, Date, Integer, Keyword
|
|||||||
|
|
||||||
|
|
||||||
class User(Document):
|
class User(Document):
|
||||||
|
creation_date = Date()
|
||||||
email = Keyword()
|
email = Keyword()
|
||||||
password_hash = Text(index=False)
|
password_hash = Text(index=False)
|
||||||
role = Keyword()
|
role = Keyword()
|
||||||
|
|
||||||
|
|
||||||
#salt = Text(index=False)
|
#salt = Text(index=False)
|
||||||
#profileImage = Text(index=False)
|
#profileImage = Text(index=False)
|
||||||
#profileImage = Keyword()
|
#profileImage = Keyword()
|
||||||
@ -32,8 +34,9 @@ class User(Document):
|
|||||||
|
|
||||||
|
|
||||||
class Chatbot(Document):
|
class Chatbot(Document):
|
||||||
|
creation_date = Date()
|
||||||
name = Text()
|
name = Text()
|
||||||
createdBy = Keyword()
|
creator_id = Keyword()
|
||||||
description = Text()
|
description = Text()
|
||||||
systemPrompt = Text(index=False)
|
systemPrompt = Text(index=False)
|
||||||
|
|
||||||
@ -45,7 +48,7 @@ class Chatbot(Document):
|
|||||||
#chatbotImage = Text(index=False)
|
#chatbotImage = Text(index=False)
|
||||||
sourceCharacters = Integer()
|
sourceCharacters = Integer()
|
||||||
|
|
||||||
#visibility = Keyword()
|
visibility = Keyword() #public, private, group?
|
||||||
#status = Keyword()
|
#status = Keyword()
|
||||||
|
|
||||||
temperature = Float()
|
temperature = Float()
|
||||||
|
173
backend/tabs.js
Executable file
173
backend/tabs.js
Executable file
@ -0,0 +1,173 @@
|
|||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
|
class TabsElement extends HTMLElement {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({mode: 'open'});
|
||||||
|
|
||||||
|
this.style_ele = document.createElement('style');
|
||||||
|
this.style_ele.innerHTML = `
|
||||||
|
|
||||||
|
.wrapper_div{
|
||||||
|
border: 1px solid #c5c5c5;
|
||||||
|
padding: 1px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_div{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab_btn{
|
||||||
|
|
||||||
|
color: red;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
|
this.shadowRoot.appendChild(this.style_ele);
|
||||||
|
|
||||||
|
this.wrapper_div = document.createElement('div');
|
||||||
|
this.wrapper_div.classList.add("wrapper_div");
|
||||||
|
|
||||||
|
this.shadowRoot.appendChild(this.wrapper_div);
|
||||||
|
this.wrapper_div.style.height = this.getAttribute('height');
|
||||||
|
|
||||||
|
this.buttonbar = document.createElement('div');
|
||||||
|
this.buttonbar.classList.add("button_div");
|
||||||
|
this.buttonbar.style.display = "flex";
|
||||||
|
this.buttonbar.style["flex-direction"] = "row";
|
||||||
|
this.wrapper_div.appendChild(this.buttonbar);
|
||||||
|
|
||||||
|
this.tabs_div = document.createElement('div');
|
||||||
|
this.wrapper_div.appendChild(this.tabs_div);
|
||||||
|
|
||||||
|
this.slot_ele = document.createElement('slot');
|
||||||
|
this.shadowRoot.appendChild(this.slot_ele);
|
||||||
|
|
||||||
|
this.id_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clearTabs(){
|
||||||
|
this.buttonbar.innerHTML = "";
|
||||||
|
this.tabs_div.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
addTab(title, content="", active=false){
|
||||||
|
let that = this;
|
||||||
|
let id = this.id_counter;
|
||||||
|
let btn = document.createElement('button');
|
||||||
|
btn.classList.add("tab_btn");
|
||||||
|
btn.setAttribute("id", "btn_"+id);
|
||||||
|
btn.onclick = ()=>{
|
||||||
|
that.setActiveTabById(id);
|
||||||
|
};
|
||||||
|
btn.innerHTML = title;
|
||||||
|
this.buttonbar.appendChild(btn);
|
||||||
|
|
||||||
|
let tab_div = document.createElement('div');
|
||||||
|
tab_div.setAttribute("id", "tab_div_"+this.id_counter);
|
||||||
|
tab_div.style.display = "none";
|
||||||
|
tab_div.innerHTML = content;
|
||||||
|
|
||||||
|
this.tabs_div.appendChild(tab_div);
|
||||||
|
if(active){
|
||||||
|
this.setActiveTabById(id);
|
||||||
|
}
|
||||||
|
this.id_counter++;
|
||||||
|
return this.id_counter - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveTabById(id){
|
||||||
|
for(let i=0; i<this.tabs_div.children.length; i++){
|
||||||
|
let tab_div = this.tabs_div.children[i];
|
||||||
|
if(tab_div.hasAttribute("id") && tab_div.getAttribute("id") === "tab_div_"+id){
|
||||||
|
this.children[i].setAttribute("selected", "selected");
|
||||||
|
tab_div.style.display = "block";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(this.children[i].hasAttribute("selected")){
|
||||||
|
this.children[i].removeAttribute("selected");
|
||||||
|
}
|
||||||
|
tab_div.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.wrapper_div.style.height = this.getAttribute('height');
|
||||||
|
let that = this;
|
||||||
|
this.slot_ele.addEventListener('slotchange', e => {
|
||||||
|
that.clearTabs();
|
||||||
|
for(let i=0; i<that.children.length; i++){
|
||||||
|
let ele = that.children[i];
|
||||||
|
this.addTab(ele.getAttribute("title"), ele.innerHTML, ele.hasAttribute("selected"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
if(name === "height"){
|
||||||
|
this.wrapper_div.style.height = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get height(){
|
||||||
|
return this.wrapper_div.style.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
set height(x){
|
||||||
|
this.wrapper_div.style.height = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neues Element definieren
|
||||||
|
customElements.define('j-tabs', TabsElement);
|
||||||
|
|
||||||
|
class TabElement extends HTMLElement {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({mode: 'open'});
|
||||||
|
this.div = document.createElement('div');
|
||||||
|
this.div.style.display = "none";
|
||||||
|
this.shadowRoot.appendChild(this.div);
|
||||||
|
this.slot_ele = document.createElement('slot');
|
||||||
|
this.div.appendChild(this.slot_ele);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get title(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
set title(x){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neues Element definieren
|
||||||
|
customElements.define('j-tab', TabElement);
|
||||||
|
|
@ -61,8 +61,8 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "11434:11434"
|
- "11434:11434"
|
||||||
volumes:
|
volumes:
|
||||||
- .:/code
|
- ..:/code
|
||||||
- ./ollama/ollama:/root/.ollama
|
- ../ollama/ollama:/root/.ollama
|
||||||
networks:
|
networks:
|
||||||
- llm_network
|
- llm_network
|
||||||
#command: "ollama pull llama2"
|
#command: "ollama pull llama2"
|
||||||
@ -72,7 +72,7 @@ services:
|
|||||||
image: ghcr.io/ollama-webui/ollama-webui:main
|
image: ghcr.io/ollama-webui/ollama-webui:main
|
||||||
container_name: ollama-webui
|
container_name: ollama-webui
|
||||||
volumes:
|
volumes:
|
||||||
- ./ollama/ollama-webui:/app/backend/data
|
- ../ollama/ollama-webui:/app/backend/data
|
||||||
depends_on:
|
depends_on:
|
||||||
- ollama
|
- ollama
|
||||||
ports:
|
ports:
|
||||||
@ -86,7 +86,6 @@ services:
|
|||||||
- llm_network
|
- llm_network
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#frontend:
|
#frontend:
|
||||||
# container_name: ${APP_PREFIX}_frontend
|
# container_name: ${APP_PREFIX}_frontend
|
||||||
# image: ${APP_PREFIX}-vue-frontend
|
# image: ${APP_PREFIX}-vue-frontend
|
||||||
@ -96,13 +95,13 @@ services:
|
|||||||
# build: ./frontend
|
# build: ./frontend
|
||||||
backend:
|
backend:
|
||||||
container_name: ${APP_PREFIX}_backend
|
container_name: ${APP_PREFIX}_backend
|
||||||
image: ${APP_PREFIX}-python-backend
|
image: registry.tobiasweise.dev/${APP_PREFIX}-python-backend
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "5000:5000"
|
||||||
env_file:
|
env_file:
|
||||||
- backend/backend.env
|
- ../backend/backend.env
|
||||||
build: ./backend
|
build: ../backend
|
||||||
networks:
|
networks:
|
||||||
- llm_network
|
- llm_network
|
||||||
depends_on:
|
depends_on:
|
44
prompt_ideas.txt
Normal file
44
prompt_ideas.txt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
system_prompt = "Always answer in English and give a short answer."
|
||||||
|
|
||||||
|
system_prompt = "Always answer in English and give a short answer. If the answer is a list give it only as a JSON array."
|
||||||
|
|
||||||
|
|
||||||
|
----------- write source code-----------------
|
||||||
|
system_prompt = "Write the answer as Prolog assertions."
|
||||||
|
|
||||||
|
|
||||||
|
Get the ministries of Germany with their suborganizations as a hierarchy expressed as a dot language source code without any further comments or explanations.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------json return value-----------
|
||||||
|
|
||||||
|
Write the answer as JSON only.
|
||||||
|
|
||||||
|
|
||||||
|
If the answer is a geographic position return a JSON-object with the longitude and latitude as attributes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----foreign languages-----
|
||||||
|
|
||||||
|
system_prompt = "Write the answer in Japanese."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user