Messages via Types with Validation etc.

This commit is contained in:
2024-10-06 15:09:55 +02:00
parent 4ea424e0d1
commit b889b581f2
36 changed files with 4274 additions and 215 deletions

21
ums/utils/__init__.py Normal file
View File

@ -0,0 +1,21 @@
# 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 ums.utils.types import (
RiddleInformation,
AgentMessage,
Riddle,
RiddleSolution,
RiddleData,
RiddleDataType,
RiddleStatus
)
from ums.utils.const import *

19
ums/utils/const.py Normal file
View File

@ -0,0 +1,19 @@
# 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
"""
This file contains shared constants.
See the content ...
"""
import os
BASE_PATH = '/ums-agent'
SHARE_PATH = os.path.join(BASE_PATH, 'share')

292
ums/utils/types.py Normal file
View File

@ -0,0 +1,292 @@
# 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
"""
This represents the basic types used to interact with the management.
The types are implemented using [pydantic](https://docs.pydantic.dev/).
It provides validation, allow JSON serialization and works well with [FastAPI](https://fastapi.tiangolo.com/) which is used internally for the http request between the agents and the management.
### Example
```python
ex = AgentMessage(
id="ex1",
riddle={
"context":"Example 1",
"question":"Get the name of the person."
},
data=[
RiddleData(
type=RiddleDataType.TEXT,
file_plain="./cv.txt"
)
]
)
ex.status.extract.required = False
```
```json
{
"id": "ex1",
"sub_ids": [],
"riddle": {
"context": "Example 1",
"question": "Get the name of the person.",
"solutions_before": []
},
"solution": null,
"data": [
{
"type": "text",
"file_plain": "/ums-agent/share/cv.txt",
"file_extracted": null
}
],
"status": {
"extract": {
"required": false,
"finished": false
},
"solve": {
"required": true,
"finished": false
},
"validate": {
"required": true,
"finished": false
},
"trial": 0,
"solved": false
}
}
```
```python
ex.solution = RiddleSolution(
solution="Otto",
explanation="Written in line 6 after 'Name:'"
)
```
```json
{
...
"solution": {
"solution": "Otto",
"explanation": "Written in line 6 after 'Name:'",
"used_data": [],
"accepted": false,
"review": null
},
...
}
```
"""
import os
from enum import Enum
from typing import List
from typing_extensions import Annotated
from pydantic import BaseModel
from pydantic.functional_validators import AfterValidator
from ums.utils.const import SHARE_PATH
class RiddleInformation(BaseModel):
"""
This is the basic class used as superclass for all message and infos
about a riddle.
"""
class RiddleDataType(Enum):
"""
Enum for the three types of data used in a riddle.
"""
TEXT = "text"
IMAGE = "image"
AUDIO = "audio"
def _check_data_file(file_name:str) -> str:
if not file_name.startswith('/'):
file_name = os.path.join(SHARE_PATH, file_name)
assert file_name.startswith(SHARE_PATH), "The data file needs to be in {}!".format(SHARE_PATH)
file_name = os.path.realpath(file_name, strict=False)
assert os.path.isfile(file_name), "The data file {} does not exist!".format(file_name)
return file_name
class RiddleData(RiddleInformation):
"""
A data item to be used to solve the riddle
"""
type: RiddleDataType
"""
The type of the data item.
"""
file_plain: Annotated[str, AfterValidator(_check_data_file)]
"""
The plain file (as path to file system) without any processing.
The path will be validated and must start with `SHARE_PATH` (or be relative to `SHARE_PATH`).
The file must exist.
"""
file_extracted: Annotated[str, AfterValidator(_check_data_file)] | None = None
"""
The processed files (as path to file system), i.e., a schematic file containing all extracted informations.
The path will be validated and must start with `SHARE_PATH` (or be relative to `SHARE_PATH`).
The file must exist.
"""
class RiddleSolution(RiddleInformation):
"""
A solution of a riddle.
"""
solution: str
"""
The textual value of the solution.
"""
explanation: str
"""
An explanation of the solution.
"""
used_data: List[RiddleData] = []
"""
The data items used to create the solution (optional).
"""
accepted : bool = False
"""
If the solution is accepted by validator/ gatekeeper.
"""
review: str | None = None
"""
A review of the solution (if None: not tried to validate)
"""
class Riddle(RiddleInformation):
"""
The riddle (the task description and possibly a solution)
"""
context: str
"""
The context of the riddle (as textual string).
"""
question: str
"""
The actual main question of the riddle (as textual string).
"""
solutions_before: List[RiddleSolution] = []
"""
If already tried to solve this riddle before, the (not accepted) solutions are stored here
"""
class RiddleSubStatus(RiddleInformation):
"""
The sub status for each possible step a riddle may go though.
"""
required: bool = True
"""
Is this step required (i.e., requested)
"""
finished: bool = False
"""
Was this step already executed.
"""
class RiddleStatus(RiddleInformation):
"""
The status of a riddle, will be mostly changed by Management when the riddle is sent to different agents while solving it.
"""
extract: RiddleSubStatus = RiddleSubStatus()
"""
The first extract step (image, text, audio -> more sematic data)
The `RiddleData` items in `AgentMessage.data` shall have `file_extracted` afterwards.
"""
solve: RiddleSubStatus = RiddleSubStatus()
"""
The *main* solving step.
`AgentMessage.solution` shall be an `RiddleSolution` afterwards.
"""
validate: RiddleSubStatus = RiddleSubStatus()
"""
The validation step, i.e., does the gatekeeper accept the solution in `AgentMessage.solution`.
"""
trial: int = 0
"""
A counter for the number of trials.
Each time the gatekeeper does not accept a solution of this riddle, the value is incremented.
"""
solved: bool = False
"""
True, after the gatekeeper accepts the solution at `AgentMessage.solution`
"""
class AgentMessage(RiddleInformation):
"""
The basic message, which is sent be the agent and the management.
The objects will be JSON en- and decoded.
"""
id: str
"""
The riddle id, e.g., ``ex1``
This is a unique string and identifies the riddle.
"""
sub_ids: List[str] = []
"""
There might be cases, when an agent decided to split in riddle in multiple *smaller* steps.
Each *sub* riddle will then get its own id (i.e., ``ex1-sub1``) while the sub id is added here as reference.
"""
riddle: Riddle
"""
The riddle to solve.
"""
solution: RiddleSolution | None = None
"""
The solution of the riddle (or empty if no solution available)
"""
data: List[RiddleData] = []
"""
The data to get the solution from.
"""
status: RiddleStatus = RiddleStatus()
"""
The status of the riddle.
"""