Agent should work

This commit is contained in:
2024-10-29 23:57:04 +01:00
parent 17d96cd069
commit 533b9fed6d
24 changed files with 703 additions and 19 deletions

View File

@ -24,8 +24,8 @@ jobs:
- name: Build the Management
run: bash ./build-mgmt.sh -no-updates
#- name: Build the Agent
# run: bash ./build-agent.sh -no-updates
- name: Build the Agent
run: bash ./build-agent.sh -no-updates
- name: Docker login
uses: docker/login-action@v3

View File

@ -12,6 +12,7 @@
- `./docker-agent/`
- `./ums/agent/`
- `./build-agent.sh`
## Development

50
build-agent.sh Executable file
View File

@ -0,0 +1,50 @@
#/bin/bash
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
# https://stackoverflow.com/a/4774063
SCRIPTPATH="$(cd -- "$(dirname "$0")" >/dev/null 2>&1; pwd -P)"
source "$SCRIPTPATH/vars.sh"
requirements="requirements-frozen.txt"
if [ "$1" != "-no-updates" ]; then
echo "Update the depencendies in requirements.txt? [may break app] (y/n)"
read newlockfile
if [ "$newlockfile" == "y" ]; then
requirements="requirements.txt"
fi;
fi;
for platform in $PLATFORMS; do
if [ "$platform" == "gpu" ]; then
platform="amd64"
tag="gpu-amd64"
else
tag="cpu-$platform"
fi;
docker build \
--pull \
--platform "linux/$platform" \
--file "$SCRIPTPATH/docker-agent/Dockerfile" \
--build-arg FROM_IMAGE="$IMAGE_REGISTRY/$IMAGE_OWNER/$IMAGE_AGENT_BASE:$tag" \
--build-arg PIP_REQ_FILE="$requirements" \
--tag "$IMAGE_REGISTRY/$IMAGE_OWNER/$IMAGE_NAME_AGENT:$tag" \
"$SCRIPTPATH"
done;
if [ "$requirements" == "requirements.txt" ]; then
# extract requirements-frozen.txt
cid=$(docker create "$IMAGE_REGISTRY/$IMAGE_OWNER/$IMAGE_NAME_AGENT:cpu-arm64")
docker cp "$cid:/ums-agenten/requirements-frozen.txt" "$SCRIPTPATH/docker-agent/requirements-frozen.txt"
docker rm "$cid"
fi;

View File

@ -8,8 +8,28 @@
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
ARG FROM_IMAGE=
ARG FROM_IMAGE
FROM $FROM_IMAGE
ARG PIP_REQ_FILE
USER root
RUN mkdir -p /ums-agenten/plattform/ && mkdir -p /ums-agenten/persist/
COPY ./docker-agent/$PIP_REQ_FILE /ums-agenten/requirements.txt
RUN pip3 install --break-system-packages --no-cache-dir -r /ums-agenten/requirements.txt \
&& pip3 freeze -q -r /ums-agenten/requirements.txt > /ums-agenten/requirements-frozen.txt
# install the code of the repo
COPY ./docker-mgmt/setup.py /ums-agenten/plattform/
RUN pip3 install --break-system-packages -e /ums-agenten/plattform/
COPY --chown=user:user ./ums/ /ums-agenten/plattform/ums/
COPY --chown=user:user ./web/ /ums-agenten/plattform/web/
WORKDIR /ums-agenten/plattform/ums/
RUN chown -R user:user /ums-agenten
USER user
ENV SERVE=true
CMD ["/usr/local/bin/uvicorn", "ums.agent.main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@ -0,0 +1,16 @@
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
# non frozen dependecies, to use latest
requests==2.32.3
fastapi==0.115.4
uvicorn==0.32.0
python-multipart==0.0.16
## The following requirements were added by pip freeze:
# ...

View File

@ -0,0 +1,15 @@
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
# non frozen dependecies, to use latest
requests
fastapi
uvicorn[standard]
python-multipart

View File

@ -20,10 +20,10 @@ services:
- 8000:80
environment:
- SOLUTION_MAX_TRIALS=5
- MANAGEMENT_URL=http://management:8000
- AGENTS_PROCESS=http://agent_process_1:3001,http://agent_process_2:3001
- AGENTS_SOLVE=http://agent_solve_1:3001
- AGENTS_GATEKEEPER=http://agent_gatekeeper_1:3001
- MANAGEMENT_URL=http://management
- AGENTS_PROCESS=http://agent_all:8000
- AGENTS_SOLVE=http://agent_all:8000
- AGENTS_GATEKEEPER=http://agent_all:8000
volumes:
- ./data/share/:/ums-agenten/share/
- ./data/persist-management/:/ums-agenten/persist/
@ -33,3 +33,21 @@ services:
# enable auto reloading (for development)
entrypoint: bash -c "nginx; SERVE=true uvicorn ums.management.main:app --uds /tmp/uvicorn.sock --proxy-headers --reload"
agent_all:
image: git.chai.uni-hamburg.de/ums-agenten/base-agent:cpu-arm64
#image: git.chai.uni-hamburg.de/ums-agenten/base-agent:cpu-amd64
#image: git.chai.uni-hamburg.de/ums-agenten/base-agent:gpu-amd64
ports:
- 8001:8000
environment:
- AGENTS_LIST=ums.example.example:AGENT_CLASSES
- MANAGEMENT_URL=http://management
volumes:
- ./data/share/:/ums-agenten/share/
- ./data/persist-all/:/ums-agenten/persist/
# bind code from host to container (for development)
- ./ums/:/ums-agenten/plattform/ums/:ro
- ./web/:/ums-agenten/plattform/web/
# enable auto reloading (for development)
entrypoint: bash -c "SERVE=true uvicorn ums.agent.main:app --host 0.0.0.0 --port 8000 --reload"

View File

@ -11,6 +11,8 @@
[supervisord]
nodaemon=true
user=root
logfile=/dev/stdout
logfile_maxbytes = 0
[program:setup]
command=/bin/sh -c "chown user:user -R /ums-agenten"

View File

@ -7,3 +7,12 @@
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
from ums.agent.agent import (
AgentCapability,
BasicAgent,
ExtractAgent,
ExtractAudioAgent, ExtractImageAgent, ExtractTextAgent,
SolveAgent,
GatekeeperAgent
)

243
ums/agent/agent.py Normal file
View File

@ -0,0 +1,243 @@
import random
from abc import abstractmethod, ABC
from enum import Enum
from typing import List, Callable
from ums.utils import (
RiddleInformation, AgentMessage, RiddleDataType, RiddleData, Riddle,
RiddleStatus, RiddleSolution,
logger
)
class AgentCapability(Enum):
"""
The three different capabilities an agent can have.
"""
EXTRACT="extract"
SOLVE="solve"
GATEKEEPER="gatekeeper"
class BasicAgent(ABC):
"""
A basic agent, each agent will be a subclass of this class.
"""
@staticmethod
@abstractmethod
def agent_capability() -> AgentCapability:
"""
Represents the capabilities of this agent, for messages/ tasks of this capability, the `handle` method will be called.
"""
pass
def __init__(self, message:AgentMessage, send_message:Callable[[AgentMessage], bool]):
self._send_message = send_message
self._sub_cnt = 0
self._message = message
self._response = message.model_copy(deep=True)
self._do_response = False
self._process()
self._respond()
@abstractmethod
def _process(self):
pass
def _respond(self):
send_it = lambda: self._send_message(self._response)
if self.before_response(self._response, send_it) and self._do_response:
send_it()
logger.debug(f"Response sent {self._response.id}")
else:
logger.debug(f"Stopped response {self._response.id}")
def before_response(self, response:AgentMessage, send_it:Callable[[], None]) -> bool:
"""
This method is called before the response is sent.
If the method returns `False` no response will be sent.
Thus, by overwriting this method, a response can be prevented.
The response to be sent is in `response` and `send_it` is a callable, which sends the response to the management if it gets called.
(Hence, one may stop sending the response and later call `send_it()` to send the response.)
"""
return True
def message(self) -> AgentMessage:
"""
Get the message this agent object is working on.
"""
return self._message;
def sub_riddle(self,
riddle:Riddle, data:List[RiddleData]=[], status:RiddleStatus=None
) -> AgentMessage|bool:
"""
Create a new sub-riddle for solving details of the current riddle.
For the sub-riddle, give a `riddle` and optionally, a selection of `data` items (default none) and a `status` (default `ums.utils.types.RiddleStatus()`).
By changing a status, different steps can be (de-)selected.
Return the message of the sub-riddle or `false` on error.
"""
if status is None:
status = RiddleStatus()
# new sub riddle id
self._sub_cnt += 1
new_id = "{}-sub-{}.{}".format(self._message.id, self._sub_cnt, int(random.random()*100))
self._message.sub_ids.append(new_id)
self._response.sub_ids.append(new_id)
# create the riddle's message
sub_msg = AgentMessage(
id=new_id,
riddle=riddle,
data=data,
status=status
)
logger.debug(f"Created sub-riddle {sub_msg.id}")
# send it
if self._send_message(sub_msg):
return sub_msg
else:
return False
@abstractmethod
def handle(self, *args:RiddleInformation) -> RiddleInformation:
"""
Handle a single task of the agent, the arguments and return value depends on the actual task (see subclass)!
**This is the method to implement!**
The full message is available via `message()`, a sub riddle can be created with `sub_riddle()`.
"""
pass
class ExtractAgent(BasicAgent):
"""
An extraction agent.
"""
def agent_capability() -> AgentCapability:
return AgentCapability.EXTRACT
@staticmethod
@abstractmethod
def extract_type() -> RiddleDataType:
"""
Represents the data this agent can process.
"""
pass
def _process(self):
for i, data in enumerate(self._response.data):
if data.type == self.__class__.extract_type():
logger.debug(f"Start extraction '{data.file_plain}'")
result = self.handle(data)
logger.debug(f"End extraction '{data.file_plain}' ('{result.file_extracted}')")
if result.file_extracted is None:
logger.info(f"Riddle {self._response.id}: 'file_extracted' for data '{data.file_plain}' still empty after handling")
self._response.data[i] = result
self._do_response = True
self._response.status.extract.finished = True
@abstractmethod
def handle(self, data:RiddleData) -> RiddleData:
"""
Process the item `data`, create extraction file and return `data` with populated `data.file_extracted`.
"""
class ExtractTextAgent(ExtractAgent):
"""
An extraction agent for text, create a subclass for your agent.
"""
def extract_type() -> RiddleDataType:
return RiddleDataType.TEXT
class ExtractAudioAgent(ExtractAgent):
"""
An extraction agent for audio, create a subclass for your agent.
"""
def extract_type() -> RiddleDataType:
return RiddleDataType.AUDIO
class ExtractImageAgent(ExtractAgent):
"""
An extraction agent for images, create a subclass for your agent.
"""
def extract_type() -> RiddleDataType:
return RiddleDataType.IMAGE
class SolveAgent(BasicAgent):
"""
A solve agent, create a subclass for your agent.
"""
def agent_capability() -> AgentCapability:
return AgentCapability.SOLVE
def _process(self):
logger.debug(f"Start solve: {self._response.id}")
solution = self.handle(self._response.riddle, self._response.data)
logger.debug(f"End solve: {self._response.id} ({solution.solution}, {solution.explanation})")
if len(solution.solution) == 0 or len(solution.explanation) == 0:
logger.info(f"Riddle {self._response.id}: Empty solution/ explanation after handling")
self._response.solution = solution
self._response.status.solve.finished = True
self._do_response = True
@abstractmethod
def handle(self, riddle:Riddle, data:RiddleData) -> RiddleSolution:
"""
Solve the `riddle` using `data` and return a solution.
"""
class GatekeeperAgent(BasicAgent):
"""
A gatekeeper agent, create a subclass for your agent.
"""
def agent_capability() -> AgentCapability:
return AgentCapability.GATEKEEPER
def _process(self):
if self._response.solution is None:
self._response.solution = RiddleSolution(solution="", explanation="")
logger.debug(f"Start validate: {self._response.id}")
solution = self.handle(self._response.solution, self._response.riddle)
logger.debug(f"End validate: {self._response.id} ({solution.review}, {solution.accepted})")
if solution.review is None or len(solution.review) == 0:
logger.info(f"Riddle {self._response.id}: Empty review after handling")
self._response.solution = solution
self._response.status.validate.finished = True
self._response.status.solved = solution.accepted
self._do_response = True
@abstractmethod
def handle(self, solution:RiddleSolution, riddle:Riddle) -> RiddleSolution:
"""
Check the `solution` of `riddle` and return solution with populated `solution.accepted` and `solution.review`.
"""

65
ums/agent/main.py Normal file
View File

@ -0,0 +1,65 @@
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
import os
from fastapi import FastAPI, Request, BackgroundTasks
from fastapi.staticfiles import StaticFiles
from fastapi.responses import JSONResponse
from ums.agent.process import MessageProcessor
from ums.utils import AgentMessage, AgentResponse, const
class WebMain():
def __init__(self):
self.msg_process = MessageProcessor()
self._init_app()
self._add_routes()
def _init_app(self):
self.app = FastAPI(
title="Agenten Plattform",
description="Agenten Plattform Agent",
openapi_url="/api/schema.json",
docs_url='/api',
redoc_url=None
)
self.app.mount(
"/static",
StaticFiles(directory=os.path.join(const.PUBLIC_PATH, 'static')),
name="static"
)
self.app.mount(
"/docs",
StaticFiles(directory=os.path.join(const.PUBLIC_PATH, 'docs'), html=True),
name="docs"
)
def _add_routes(self):
@self.app.get("/", response_class=JSONResponse, summary="Link list")
def index():
return {
"title" : "Agenten Plattform Agent",
"./message" : "Messaged from the Management",
"./api" : "API Overview",
"./docs" : "Documentation"
}
@self.app.post("/message", summary="Send a message to this agent")
def message(request: Request, message:AgentMessage, background_tasks: BackgroundTasks) -> AgentResponse:
return self.msg_process.new_message(message, background_tasks)
if __name__ == "ums.agent.main" and os.environ.get('SERVE', 'false') == 'true':
main = WebMain()
app = main.app

95
ums/agent/process.py Normal file
View File

@ -0,0 +1,95 @@
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
import os, importlib
from typing import List
import requests
from fastapi import BackgroundTasks
from ums.agent.agent import BasicAgent, AgentCapability, ExtractAgent, SolveAgent, GatekeeperAgent
from ums.utils import AgentMessage, AgentResponse, logger
class MessageProcessor():
MANAGEMENT_URL = os.environ.get('MANAGEMENT_URL', 'http://127.0.0.1:80').strip().strip('/')
AGENTS_LIST = os.environ.get('AGENTS_LIST', 'ums.example.example:AGENT_CLASSES').strip()
def __init__(self):
self.counts = 0
module_name, var_name = self.AGENTS_LIST.split(':')
agents_module = importlib.import_module(module_name)
self.agent_classes:List[BasicAgent] = getattr(agents_module, var_name)
self.extract_agents:List[ExtractAgent] = list(filter(
lambda ac: ac.agent_capability() == AgentCapability.EXTRACT,
self.agent_classes
))
self.solve_agents:List[SolveAgent] = list(filter(
lambda ac: ac.agent_capability() == AgentCapability.SOLVE,
self.agent_classes
))
self.gatekeeper_agents:List[GatekeeperAgent] = list(filter(
lambda ac: ac.agent_capability() == AgentCapability.GATEKEEPER,
self.agent_classes
))
def new_message(self, message:AgentMessage, background_tasks: BackgroundTasks) -> AgentResponse:
enqueued = False
if message.status.extract.required and not message.status.extract.finished:
# send to extract agents
if len(self.extract_agents) > 0:
data_types = set( d.type for d in message.data )
for ac in self.extract_agents:
if ac.extract_type() in data_types:
background_tasks.add_task(ac, message, self._send_message)
enqueued = True
elif message.status.solve.required and not message.status.solve.finished:
# send to solve agents
if len(self.solve_agents) > 0:
for sa in self.solve_agents:
background_tasks.add_task(sa, message, self._send_message)
enqueued = True
elif message.status.validate.required and not message.status.validate.finished:
# send to solve agents
if len(self.gatekeeper_agents) > 0:
for ga in self.gatekeeper_agents:
background_tasks.add_task(ga, message, self._send_message)
enqueued = True
logger.debug(
("Added to queue" if enqueued else "No agent found to queue message.") +
f"ID: {message.id} Count: {self.counts}"
)
self.counts += 1
return AgentResponse(
count=self.counts-1,
msg="Added to queue" if enqueued else "",
error=not enqueued,
error_msg=None if enqueued else "No agent found to queue message."
)
def _send_message(self, message:AgentMessage) -> bool:
r = requests.post(
"{}/message".format(self.MANAGEMENT_URL),
data=message.model_dump_json(),
headers={"accept" : "application/json", "content-type" : "application/json"}
)
if r.status_code == 200:
return True
else:
logger.warning(f"Error sending message to management! {(r.text, r.headers)}")
return False

9
ums/example/__init__.py Normal file
View File

@ -0,0 +1,9 @@
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt

View File

@ -11,6 +11,8 @@
if __name__ == "__main__":
## Example: Sending messages to management via python
from ums.utils import AgentMessage, RiddleData, RiddleDataType, RiddleSolution, ManagementRequest
ex = AgentMessage(

71
ums/example/example.py Normal file
View File

@ -0,0 +1,71 @@
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
from typing import Callable
from ums.agent import ExtractAudioAgent, ExtractImageAgent, ExtractTextAgent, SolveAgent, GatekeeperAgent
from ums.utils.types import AgentMessage, Riddle, RiddleData, RiddleSolution, RiddleStatus
"""
Examples for simple agents.
Each agent is represented by its own class. The handling of tasks is done by `handle()` in each agent.
Finally `AGENT_CLASSES` contains the classes of the agents in a list. Via environmental variables this list is specified to the ums.agent system.
"""
class MyExtractAudioAgent(ExtractAudioAgent):
def handle(self, data: RiddleData) -> RiddleData:
print("Audio Process:", data.file_plain)
return data
class MyExtractImageAgent(ExtractImageAgent):
def handle(self, data: RiddleData) -> RiddleData:
print("Image Process:", data.file_plain)
return data
class MyExtractTextAgent(ExtractTextAgent):
def before_response(self, response: AgentMessage, send_it: Callable[[], None]) -> bool:
print("The response will be:", response)
return True
def handle(self, data: RiddleData) -> RiddleData:
print("Text Process:", data.file_plain)
return data
class MySolveAgent(SolveAgent):
def handle(self, riddle: Riddle, data: RiddleData) -> RiddleSolution:
if self.message().id == "test":
status = RiddleStatus()
status.extract.required = False
self.sub_riddle(riddle=Riddle(context="Haha", question="Blubber"), status=status)
return RiddleSolution(solution="Huii", explanation="Blubb")
class MyGatekeeperAgent(GatekeeperAgent):
def handle(self, solution: RiddleSolution, riddle: Riddle) -> RiddleSolution:
solution.accepted = True
solution.review = "Ok"
return solution
AGENT_CLASSES = [
MyExtractAudioAgent, MyExtractImageAgent, MyExtractTextAgent,
MySolveAgent,
MyGatekeeperAgent
]

View File

@ -15,7 +15,7 @@ import requests
from fastapi import BackgroundTasks
from ums.management.db import DB
from ums.utils import AgentMessage, AgentResponse
from ums.utils import AgentMessage, AgentResponse, logger
class MessageProcessor():
@ -41,11 +41,11 @@ class MessageProcessor():
self.management_name = self._get_name(self.MANAGEMENT_URL)
if len(self.AGENTS_PROCESS) == 0:
print("Not Process Agent (AGENTS_PROCESS) found, this may be a problem!")
logger.warning(f"Not Process Agent (AGENTS_PROCESS) found, this may be a problem!")
if len(self.AGENTS_SOLVE) == 0:
print("Not Solve Agent (AGENTS_SOLVE) found, this may be a problem!")
logger.warning(f"Not Solve Agent (AGENTS_SOLVE) found, this may be a problem!")
if len(self.AGENTS_GATEKEEPER) == 0:
print("Not Gatekeeper Agent (AGENTS_GATEKEEPER) found, this may be a problem!")
logger.warning(f"Not Gatekeeper Agent (AGENTS_GATEKEEPER) found, this may be a problem!")
def _get_name(self, url:str) -> str:
m = re.match(r'^https?://([^:]*)(?::(\d+))?$', url)
@ -92,6 +92,11 @@ class MessageProcessor():
self._send_messages(self.AGENTS_GATEKEEPER, db_message.message)
else: # all steps "done"
# validate not required? (then solved will never be set to true, thus set it here)
if not db_message.message.status.validate.required:
db_message.message.status.solved = True
if db_message.message.status.solved:
# yay, message is solved
self.db.set_solution(count=count, solution=True);
@ -155,6 +160,6 @@ class MessageProcessor():
self.db.set_processed(db_count, processed=True)
return True
else:
print("Error sending message to:", recipient, (r.text, r.headers))
logger.warning(f"Error sending message to: {recipient} {(r.text, r.headers)}")
return False

View File

@ -8,6 +8,21 @@
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
from ums.utils.const import *
import logging, os
if os.environ.get('SERVE', 'false') == 'true':
logging.basicConfig(
handlers=[
logging.FileHandler(LOG_FILE),
logging.StreamHandler()
],
level=LOG_LEVEL,
format='%(asctime)s %(levelname)s %(name)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger('UMS Agenten')
from ums.utils.types import (
RiddleInformation,
AgentMessage,
@ -20,8 +35,6 @@ from ums.utils.types import (
MessageDbRow
)
from ums.utils.const import *
from ums.utils.request import ManagementRequest
from ums.utils.functions import list_shared_data, list_shared_schema

View File

@ -13,9 +13,13 @@
See the content ...
"""
import os
import os, logging
BASE_PATH = '/ums-agenten'
SHARE_PATH = os.path.join(BASE_PATH, 'share')
PERSIST_PATH = os.path.join(BASE_PATH, 'persist')
PUBLIC_PATH = os.path.join(BASE_PATH, 'plattform', 'web', 'public')
TEMPLATE_PATH = os.path.join(BASE_PATH, 'plattform', 'web', 'templates')
LOG_FILE = os.path.join(PERSIST_PATH, 'ums.log')
LOG_LEVEL = logging.INFO

View File

@ -1,3 +1,13 @@
# Agenten Plattform
#
# (c) 2024 Magnus Bender
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
# Universitaet Hamburg
# https://www.chai.uni-hamburg.de/~bender
#
# source code released under the terms of GNU Public License Version 3
# https://www.gnu.org/licenses/gpl-3.0.txt
import os
from typing import List, Callable

View File

@ -13,5 +13,6 @@
IMAGE_REGISTRY="git.chai.uni-hamburg.de"
IMAGE_OWNER="ums-agenten"
IMAGE_NAME_AGENT="base-agent"
IMAGE_AGENT_BASE="base-image"
IMAGE_NAME_MGMT="management"
PLATFORMS="amd64 arm64 gpu"

View File

@ -1,3 +1,11 @@
/** Agenten Plattform
(c) 2024 Magnus Bender
Institute of Humanities-Centered Artificial Intelligence (CHAI)
Universitaet Hamburg
https://www.chai.uni-hamburg.de/~bender
source code released under the terms of GNU Public License Version 3
https://www.gnu.org/licenses/gpl-3.0.txt
**/
.value_filter {
width: 150px;

View File

@ -1,3 +1,12 @@
/** Agenten Plattform
(c) 2024 Magnus Bender
Institute of Humanities-Centered Artificial Intelligence (CHAI)
Universitaet Hamburg
https://www.chai.uni-hamburg.de/~bender
source code released under the terms of GNU Public License Version 3
https://www.gnu.org/licenses/gpl-3.0.txt
**/
var fileLast = 0;
function renderFile(cnt, type="", file_extracted="", file_plain=""){
var template = $("#filesTemplate").html();

View File

@ -1,3 +1,12 @@
/** Agenten Plattform
(c) 2024 Magnus Bender
Institute of Humanities-Centered Artificial Intelligence (CHAI)
Universitaet Hamburg
https://www.chai.uni-hamburg.de/~bender
source code released under the terms of GNU Public License Version 3
https://www.gnu.org/licenses/gpl-3.0.txt
**/
function pagination_link(name, value){
let link_args = db_args;
if (name && value){

View File

@ -1,3 +1,12 @@
{#-
Agenten Plattform
(c) 2024 Magnus Bender
Institute of Humanities-Centered Artificial Intelligence (CHAI)
Universitaet Hamburg
https://www.chai.uni-hamburg.de/~bender
source code released under the terms of GNU Public License Version 3
https://www.gnu.org/licenses/gpl-3.0.txt
-#}
<div class="modal" tabindex="-1" id="message_sent">
<div class="modal-dialog">
<div class="modal-content">