feat: add psagentai agent with deployment and configuration
Initial project setup including the main agent script, configuration files, profile JSON state, systemd service unit, startup script, deployment documentation, and Python dependencies. Includes .env, venv/, and __pycache__/ which should likely be excluded via .gitignore to avoid committing secrets and artifacts.
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
"""Relationship-related objects."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, cast
|
||||
|
||||
from docx.opc.oxml import CT_Relationships
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from docx.opc.part import Part
|
||||
|
||||
|
||||
class Relationships(Dict[str, "_Relationship"]):
|
||||
"""Collection object for |_Relationship| instances, having list semantics."""
|
||||
|
||||
def __init__(self, baseURI: str):
|
||||
super(Relationships, self).__init__()
|
||||
self._baseURI = baseURI
|
||||
self._target_parts_by_rId: dict[str, Any] = {}
|
||||
|
||||
def add_relationship(
|
||||
self, reltype: str, target: Part | str, rId: str, is_external: bool = False
|
||||
) -> "_Relationship":
|
||||
"""Return a newly added |_Relationship| instance."""
|
||||
rel = _Relationship(rId, reltype, target, self._baseURI, is_external)
|
||||
self[rId] = rel
|
||||
if not is_external:
|
||||
self._target_parts_by_rId[rId] = target
|
||||
return rel
|
||||
|
||||
def get_or_add(self, reltype: str, target_part: Part) -> _Relationship:
|
||||
"""Return relationship of `reltype` to `target_part`, newly added if not already
|
||||
present in collection."""
|
||||
rel = self._get_matching(reltype, target_part)
|
||||
if rel is None:
|
||||
rId = self._next_rId
|
||||
rel = self.add_relationship(reltype, target_part, rId)
|
||||
return rel
|
||||
|
||||
def get_or_add_ext_rel(self, reltype: str, target_ref: str) -> str:
|
||||
"""Return rId of external relationship of `reltype` to `target_ref`, newly added
|
||||
if not already present in collection."""
|
||||
rel = self._get_matching(reltype, target_ref, is_external=True)
|
||||
if rel is None:
|
||||
rId = self._next_rId
|
||||
rel = self.add_relationship(reltype, target_ref, rId, is_external=True)
|
||||
return rel.rId
|
||||
|
||||
def part_with_reltype(self, reltype: str) -> Part:
|
||||
"""Return target part of rel with matching `reltype`, raising |KeyError| if not
|
||||
found and |ValueError| if more than one matching relationship is found."""
|
||||
rel = self._get_rel_of_type(reltype)
|
||||
return rel.target_part
|
||||
|
||||
@property
|
||||
def related_parts(self):
|
||||
"""Dict mapping rIds to target parts for all the internal relationships in the
|
||||
collection."""
|
||||
return self._target_parts_by_rId
|
||||
|
||||
@property
|
||||
def xml(self) -> str:
|
||||
"""Serialize this relationship collection into XML suitable for storage as a
|
||||
.rels file in an OPC package."""
|
||||
rels_elm = CT_Relationships.new()
|
||||
for rel in self.values():
|
||||
rels_elm.add_rel(rel.rId, rel.reltype, rel.target_ref, rel.is_external)
|
||||
return rels_elm.xml
|
||||
|
||||
def _get_matching(
|
||||
self, reltype: str, target: Part | str, is_external: bool = False
|
||||
) -> _Relationship | None:
|
||||
"""Return relationship of matching `reltype`, `target`, and `is_external` from
|
||||
collection, or None if not found."""
|
||||
|
||||
def matches(rel: _Relationship, reltype: str, target: Part | str, is_external: bool):
|
||||
if rel.reltype != reltype:
|
||||
return False
|
||||
if rel.is_external != is_external:
|
||||
return False
|
||||
rel_target = rel.target_ref if rel.is_external else rel.target_part
|
||||
return rel_target == target
|
||||
|
||||
for rel in self.values():
|
||||
if matches(rel, reltype, target, is_external):
|
||||
return rel
|
||||
return None
|
||||
|
||||
def _get_rel_of_type(self, reltype: str):
|
||||
"""Return single relationship of type `reltype` from the collection.
|
||||
|
||||
Raises |KeyError| if no matching relationship is found. Raises |ValueError| if
|
||||
more than one matching relationship is found.
|
||||
"""
|
||||
matching = [rel for rel in self.values() if rel.reltype == reltype]
|
||||
if len(matching) == 0:
|
||||
tmpl = "no relationship of type '%s' in collection"
|
||||
raise KeyError(tmpl % reltype)
|
||||
if len(matching) > 1:
|
||||
tmpl = "multiple relationships of type '%s' in collection"
|
||||
raise ValueError(tmpl % reltype)
|
||||
return matching[0]
|
||||
|
||||
@property
|
||||
def _next_rId(self) -> str: # pyright: ignore[reportReturnType]
|
||||
"""Next available rId in collection, starting from 'rId1' and making use of any
|
||||
gaps in numbering, e.g. 'rId2' for rIds ['rId1', 'rId3']."""
|
||||
for n in range(1, len(self) + 2):
|
||||
rId_candidate = "rId%d" % n # like 'rId19'
|
||||
if rId_candidate not in self:
|
||||
return rId_candidate
|
||||
|
||||
|
||||
class _Relationship:
|
||||
"""Value object for relationship to part."""
|
||||
|
||||
def __init__(
|
||||
self, rId: str, reltype: str, target: Part | str, baseURI: str, external: bool = False
|
||||
):
|
||||
super(_Relationship, self).__init__()
|
||||
self._rId = rId
|
||||
self._reltype = reltype
|
||||
self._target = target
|
||||
self._baseURI = baseURI
|
||||
self._is_external = bool(external)
|
||||
|
||||
@property
|
||||
def is_external(self) -> bool:
|
||||
return self._is_external
|
||||
|
||||
@property
|
||||
def reltype(self) -> str:
|
||||
return self._reltype
|
||||
|
||||
@property
|
||||
def rId(self) -> str:
|
||||
return self._rId
|
||||
|
||||
@property
|
||||
def target_part(self) -> Part:
|
||||
if self._is_external:
|
||||
raise ValueError(
|
||||
"target_part property on _Relationship is undefined when target mode is External"
|
||||
)
|
||||
return cast("Part", self._target)
|
||||
|
||||
@property
|
||||
def target_ref(self) -> str:
|
||||
if self._is_external:
|
||||
return cast(str, self._target)
|
||||
else:
|
||||
target = cast("Part", self._target)
|
||||
return target.partname.relative_ref(self._baseURI)
|
||||
Reference in New Issue
Block a user