pyxecm 3.0.1__py3-none-any.whl → 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyxecm might be problematic. Click here for more details.

Files changed (52) hide show
  1. pyxecm/avts.py +4 -4
  2. pyxecm/coreshare.py +14 -15
  3. pyxecm/helper/data.py +2 -1
  4. pyxecm/helper/web.py +11 -11
  5. pyxecm/helper/xml.py +41 -10
  6. pyxecm/otac.py +1 -1
  7. pyxecm/otawp.py +19 -19
  8. pyxecm/otca.py +870 -67
  9. pyxecm/otcs.py +1567 -280
  10. pyxecm/otds.py +332 -153
  11. pyxecm/otkd.py +4 -4
  12. pyxecm/otmm.py +1 -1
  13. pyxecm/otpd.py +246 -30
  14. {pyxecm-3.0.1.dist-info → pyxecm-3.1.0.dist-info}/METADATA +2 -1
  15. pyxecm-3.1.0.dist-info/RECORD +82 -0
  16. pyxecm_api/app.py +45 -35
  17. pyxecm_api/auth/functions.py +2 -2
  18. pyxecm_api/auth/router.py +2 -3
  19. pyxecm_api/common/functions.py +164 -12
  20. pyxecm_api/settings.py +0 -8
  21. pyxecm_api/terminal/router.py +1 -1
  22. pyxecm_api/v1_csai/router.py +33 -18
  23. pyxecm_customizer/browser_automation.py +98 -48
  24. pyxecm_customizer/customizer.py +43 -25
  25. pyxecm_customizer/guidewire.py +422 -8
  26. pyxecm_customizer/k8s.py +23 -27
  27. pyxecm_customizer/knowledge_graph.py +501 -20
  28. pyxecm_customizer/m365.py +45 -44
  29. pyxecm_customizer/payload.py +1684 -1159
  30. pyxecm_customizer/payload_list.py +3 -0
  31. pyxecm_customizer/salesforce.py +122 -79
  32. pyxecm_customizer/servicenow.py +27 -7
  33. pyxecm_customizer/settings.py +3 -1
  34. pyxecm_customizer/successfactors.py +2 -2
  35. pyxecm_customizer/translate.py +1 -1
  36. pyxecm-3.0.1.dist-info/RECORD +0 -96
  37. pyxecm_api/agents/__init__.py +0 -7
  38. pyxecm_api/agents/app.py +0 -13
  39. pyxecm_api/agents/functions.py +0 -119
  40. pyxecm_api/agents/models.py +0 -10
  41. pyxecm_api/agents/otcm_knowledgegraph/__init__.py +0 -1
  42. pyxecm_api/agents/otcm_knowledgegraph/functions.py +0 -85
  43. pyxecm_api/agents/otcm_knowledgegraph/models.py +0 -61
  44. pyxecm_api/agents/otcm_knowledgegraph/router.py +0 -74
  45. pyxecm_api/agents/otcm_user_agent/__init__.py +0 -1
  46. pyxecm_api/agents/otcm_user_agent/models.py +0 -20
  47. pyxecm_api/agents/otcm_user_agent/router.py +0 -65
  48. pyxecm_api/agents/otcm_workspace_agent/__init__.py +0 -1
  49. pyxecm_api/agents/otcm_workspace_agent/models.py +0 -40
  50. pyxecm_api/agents/otcm_workspace_agent/router.py +0 -200
  51. {pyxecm-3.0.1.dist-info → pyxecm-3.1.0.dist-info}/WHEEL +0 -0
  52. {pyxecm-3.0.1.dist-info → pyxecm-3.1.0.dist-info}/entry_points.txt +0 -0
@@ -1,7 +0,0 @@
1
- """init Module."""
2
-
3
- from .otcm_knowledgegraph.router import router as otcm_knowledgegraph_router
4
- from .otcm_user_agent.router import router as otcm_user_agent_router
5
- from .otcm_workspace_agent.router import router as otcm_workspace_agent_router
6
-
7
- __all__ = ["otcm_knowledgegraph_router", "otcm_user_agent_router", "otcm_workspace_agent_router"]
pyxecm_api/agents/app.py DELETED
@@ -1,13 +0,0 @@
1
- """List of all routers for the csai agents."""
2
-
3
- import logging
4
-
5
- from fastapi import APIRouter
6
-
7
- from . import otcm_knowledgegraph_router, otcm_user_agent_router, otcm_workspace_agent_router
8
-
9
- logger = logging.getLogger("pyxecm_api.agents")
10
-
11
- router = APIRouter(tags=["csai agents"])
12
-
13
- agent_routers = [router, otcm_workspace_agent_router, otcm_user_agent_router, otcm_knowledgegraph_router]
@@ -1,119 +0,0 @@
1
- """List of all routers for the csai agents."""
2
-
3
- import logging
4
- from importlib.metadata import version
5
- from threading import Thread
6
-
7
- from fastapi.openapi.utils import get_openapi
8
- from fastapi.routing import APIRoute
9
- from pyxecm import OTCA, OTCS
10
- from pyxecm_customizer.settings import Settings
11
-
12
- from pyxecm_api.common.functions import get_k8s_object, get_otcs_object
13
-
14
- logger = logging.getLogger("pyxecm_api.agents")
15
-
16
- SETTINGS = Settings()
17
-
18
-
19
- def get_otca_object(otcs_object: OTCS | None = None) -> OTCA:
20
- """Get the OTCA object."""
21
-
22
- k8s_object = get_k8s_object()
23
- content_system = {}
24
- for service in ["chat", "embed"]:
25
- cm = k8s_object.get_config_map(f"csai-{service}-svc")
26
- if cm:
27
- content_system[service] = cm.data.get("CONTENT_SYSTEM", "none")
28
-
29
- otca = OTCA(
30
- chat_url=str(SETTINGS.aviator.chat_svc_url),
31
- embed_url=str(SETTINGS.aviator.embed_svc_url),
32
- studio_url=str(SETTINGS.aviator.studio_url),
33
- otds_url=str(SETTINGS.otds.url_internal),
34
- client_id=SETTINGS.aviator.oauth_client,
35
- client_secret=SETTINGS.aviator.oauth_secret,
36
- otcs_object=otcs_object,
37
- content_system=content_system,
38
- logger=logger.getChild("otca"),
39
- )
40
-
41
- return otca
42
-
43
-
44
- # end function
45
-
46
-
47
- def register_tool_body(route: APIRoute, agents: list[str] | None = None) -> dict:
48
- """Generate the request body for the CSAI Studio integration.
49
-
50
- Args:
51
- route (APIRoute):
52
- The API routes.
53
- agents (list[str] | None, optional):
54
- A list of agents this tool is associated with.
55
- Defaults to ["retrieverAgent"].
56
-
57
- Returns:
58
- dict:
59
- The body for the REST API call to register a tool in Content Aviator.
60
-
61
- """
62
-
63
- if agents is None:
64
- agents = ["retrieverAgent"]
65
-
66
- return {
67
- "name": route.name,
68
- "description": route.description,
69
- "APISchema": get_openapi(
70
- title=route.name,
71
- openapi_version="3.0.0",
72
- version=version("pyxecm"),
73
- servers=[{"url": "http://customizer:8000"}],
74
- routes=[route],
75
- ),
76
- "requestTemplate": {
77
- "data": {"context": {"where": "memory.input.where", "query": "memory.input.query"}},
78
- },
79
- "responseTemplate": {}, # Optional response template used to filter the response from the API.
80
- "agents": agents,
81
- }
82
-
83
-
84
- # end function
85
-
86
-
87
- def register_all() -> None:
88
- """Register all tools."""
89
-
90
- def register_all_routes() -> None:
91
- otca = get_otca_object(otcs_object=get_otcs_object())
92
- response = otca.do_request(
93
- method="POST",
94
- url=str(SETTINGS.aviator.studio_url) + "studio/v1/import",
95
- show_error=False,
96
- parse_request_response=False,
97
- )
98
- if not response or response.text != "Accepted":
99
- logger.error("Failed to import tools!")
100
- return
101
- get_k8s_object().restart_deployment(deployment_name="csai-chat-svc")
102
-
103
- for route in routes:
104
- logger.info("Registering Content Aviator tool -> '%s'...", route.name)
105
- request_body = register_tool_body(route=route)
106
- result = otca.register_tool(request_body=request_body)
107
- if result:
108
- logger.info("Tool got registered successfully -> %s", route.name)
109
- logger.debug("%s", result)
110
-
111
- from pyxecm_api.app import app
112
-
113
- # Get all FastAPI routes and register them as tools:
114
- routes: list[APIRoute] = [route for route in app.routes if route.path.startswith("/agents")]
115
-
116
- Thread(target=register_all_routes, name="RegisterTools").start()
117
-
118
-
119
- # end function
@@ -1,10 +0,0 @@
1
- """Define commonly used models."""
2
-
3
- from pydantic import BaseModel
4
-
5
-
6
- class Context(BaseModel):
7
- """Define Model that is used to provide static context information for tools."""
8
-
9
- where: list[dict]
10
- query: str
@@ -1 +0,0 @@
1
- """init module."""
@@ -1,85 +0,0 @@
1
- """Function to build a knowledge graph from the OTCS object."""
2
-
3
- import logging
4
- import time
5
- from datetime import UTC, datetime
6
-
7
- from pyxecm_customizer.knowledge_graph import KnowledgeGraph
8
-
9
- from pyxecm_api.common.functions import get_otcs_object
10
-
11
- logger = logging.getLogger("pyxecm_api.agent.otcm_knowledge_graph")
12
-
13
- KNOWLEDGEGRAPH_OBJECT: KnowledgeGraph = None
14
- KNOWLEDGEGRAPH_ONTOLOGY = {
15
- ("Vendor", "Material", "child"): ["offers", "supplies", "provides"],
16
- ("Vendor", "Purchase Order", "child"): ["supplies", "provides"],
17
- ("Vendor", "Purchase Contract", "child"): ["signs", "owns"],
18
- ("Material", "Vendor", "parent"): ["is supplied by"],
19
- ("Purchase Order", "Material", "child"): ["includes", "is part of"],
20
- ("Customer", "Sales Order", "child"): ["has ordered"],
21
- ("Customer", "Sales Contract", "child"): ["signs", "owns"],
22
- ("Sales Order", "Customer", "parent"): ["belongs to", "is initiated by"],
23
- ("Sales Order", "Material", "child"): ["includes", "consists of"],
24
- ("Sales Order", "Delivery", "child"): ["triggers", "is followed by"],
25
- ("Sales Order", "Production Order", "child"): ["triggers", "is followed by"],
26
- ("Sales Contract", "Material", "child"): ["includes", "consists of"],
27
- ("Production Order", "Material", "child"): ["includes", "consists of"],
28
- ("Production Order", "Delivery", "child"): ["triggers", "is followed by"],
29
- ("Production Order", "Goods Movement", "child"): ["triggers", "is followed by"],
30
- ("Delivery", "Goods Movement", "child"): ["triggers", "is followed by"],
31
- ("Delivery", "Material", "child"): ["triggers", "is followed by"],
32
- }
33
-
34
-
35
- def build_graph() -> None:
36
- """Build the knowledgeGraph. And keep it updated every hour."""
37
-
38
- def build() -> None:
39
- knowledge_graph = KnowledgeGraph(otcs_object=get_otcs_object(), ontology=KNOWLEDGEGRAPH_ONTOLOGY)
40
-
41
- start_time = datetime.now(UTC)
42
- result = knowledge_graph.build_graph(
43
- workspace_type_exclusions=None,
44
- workspace_type_inclusions=[
45
- "Vendor",
46
- "Purchase Contract",
47
- "Purchase Order",
48
- "Material",
49
- "Customer",
50
- "Sales Order",
51
- "Sales Contract",
52
- "Delivery",
53
- "Goods Movement",
54
- ],
55
- workers=20,
56
- filter_at_traversal=True, # also filter for workspace types if following relationships
57
- relationship_types=["child"], # only go from parent to child
58
- strategy="BFS",
59
- metadata=False,
60
- )
61
- end_time = datetime.now(UTC)
62
- logger.info(
63
- "Knowledge Graph completed in %s. Processed %d workspace nodes and traversed %d workspace relationships.",
64
- str(end_time - start_time),
65
- result["processed"],
66
- result["traversed"],
67
- )
68
-
69
- global KNOWLEDGEGRAPH_OBJECT # noqa: PLW0603
70
- KNOWLEDGEGRAPH_OBJECT = knowledge_graph
71
-
72
- while True:
73
- logger.info("Building knowledge graph...")
74
- build()
75
- logger.info("Knowledge graph build complete. Waiting for 1 hour before rebuilding...")
76
- time.sleep(3600)
77
-
78
-
79
- ###
80
-
81
-
82
- def get_knowledgegraph_object() -> KnowledgeGraph:
83
- """Get the OTCA object."""
84
-
85
- return KNOWLEDGEGRAPH_OBJECT
@@ -1,61 +0,0 @@
1
- """Define Models for KnowledgeGraph."""
2
-
3
- from typing import Annotated, Literal
4
-
5
- from pydantic import BaseModel, Field
6
-
7
- from pyxecm_api.agents.models import Context
8
- from pyxecm_api.agents.otcm_workspace_agent.models import WorkspaceModel
9
-
10
-
11
- class KnowledgeGraphQueryModel(BaseModel):
12
- """Defines Model for describing workspaces in OTCM (Opentext Content Management).
13
-
14
- To display an instance of this model, please display the link.
15
- """
16
-
17
- source_type: Annotated[
18
- str,
19
- Field(
20
- description="Source workspace type name provided in the user query. This provides the start point of the query in the Knowledge Graph."
21
- ),
22
- ]
23
- source_value: Annotated[
24
- str,
25
- Field(
26
- description="Name of the source workspace instance (of the type provided by source_type). This is NOT optional. If it is missing then you need to switch source and target. The source always needs to have a specific value!"
27
- ),
28
- ]
29
- target_type: Annotated[
30
- str,
31
- Field(
32
- description="Target workspace type name provided in the user query. This defines the result workspace types of the Knolwedge Graph query."
33
- ),
34
- ] = None
35
- target_value: Annotated[
36
- str,
37
- Field(
38
- description="Name of the target workspace instance (of the type provided by the target_type). This value is optionalis an additional / optional selection criteria."
39
- ),
40
- ] = None
41
- intermediate_types: Annotated[
42
- list[str],
43
- Field(
44
- description="List of workspace types (entities) that are on the path between the source workspace type and target workspace type. These are typically additional relational conditions the user is giving in the query. The start type and target types should never be in intermediate types!"
45
- ),
46
- ] = None
47
- direction: Annotated[
48
- Literal["child", "parent"],
49
- Field(
50
- description="The direction the graph should be traversed in. This is either 'child' (the default) or 'parent'. 'child' is typically used if traversing from main entities to related sub-entities thus following a '1 -> many' relationship (top-down). 'parent' is typically used if traversing bottom-up. This should reflect the relationship between the source type of the workspace and the requested target type. A 'child' relationship is e.g. 'Customer' -> 'Sales Order'. A 'parent' relationship is e.g. 'Material' -> 'Sales Order'."
51
- ),
52
- ] = None
53
-
54
-
55
- class KnowledgeGraphResponseModel(BaseModel):
56
- """Response of the Knowledge Graph tool."""
57
-
58
- results: Annotated[list[WorkspaceModel], Field(description="A list of workspaces matching the query.")]
59
- context_update: Annotated[
60
- Context, Field(description="An update for the LLM context. Update the graph state with this information!")
61
- ]
@@ -1,74 +0,0 @@
1
- """Define router for workspace endpoints."""
2
-
3
- import logging
4
- from typing import Annotated
5
-
6
- from fastapi import APIRouter, Body, Depends, HTTPException
7
- from pyxecm_customizer.knowledge_graph import KnowledgeGraph
8
-
9
- from pyxecm_api.agents.models import Context
10
- from pyxecm_api.agents.otcm_workspace_agent.models import WorkspaceModel
11
-
12
- from .functions import (
13
- KNOWLEDGEGRAPH_ONTOLOGY,
14
- get_knowledgegraph_object,
15
- )
16
- from .models import (
17
- KnowledgeGraphQueryModel,
18
- )
19
-
20
- router = APIRouter(prefix="/otcm_knowledgegraph_agent", tags=["csai agents"])
21
-
22
- logger = logging.getLogger("pyxecm_api.agents.otcm_knowledgegraph_agent")
23
-
24
-
25
- @router.post(
26
- path="/query",
27
- summary="Query the knowledge graph for a list of workspaces matching the user query.",
28
- description=f"Use the following ontology to understand the relationship between workspace types and the direction of the relationships (either 'parent' or 'child'): {KNOWLEDGEGRAPH_ONTOLOGY}",
29
- responses={
30
- 200: {"description": "Workspaces found"},
31
- 403: {"description": "Invalid credentials"},
32
- 404: {"description": "No matching workspaces found"},
33
- 500: {"description": "Knowledge Graph is not available"},
34
- },
35
- # response_model=ToolResponse,
36
- # response_model=list[WorkspaceModel],
37
- response_description="List of workspaces that match the query. Best presented as a list with hyperlinks to the workspace.",
38
- )
39
- def otcm_knowledgegraph_query(
40
- context: Context,
41
- knowledge_graph: Annotated[KnowledgeGraph, Depends(get_knowledgegraph_object)],
42
- knowledge_graph_query: Annotated[KnowledgeGraphQueryModel, Body()],
43
- ) -> dict | None: # ToolResponse | None: # list[WorkspaceModel] | None:
44
- # ) -> list[WorkspaceModel] | None:
45
- """Query the knowledge graph for a list of workspaces matching the user query. Workspaces are entities and workspace types are entitiy types."""
46
-
47
- if not knowledge_graph:
48
- raise HTTPException(status_code=500, detail="Knowledge Graph is not available")
49
-
50
- logger.info("Got context -> %s", context)
51
- results = knowledge_graph.graph_query(
52
- source_type=knowledge_graph_query.source_type,
53
- source_value=knowledge_graph_query.source_value,
54
- intermediate_types=knowledge_graph_query.intermediate_types,
55
- target_type=knowledge_graph_query.target_type,
56
- target_value=knowledge_graph_query.target_value,
57
- direction=knowledge_graph_query.direction,
58
- max_hops=4,
59
- )
60
- if not results:
61
- raise HTTPException(status_code=404, detail="No result found")
62
-
63
- where_clause = [{"workspaceID": str(workspace[1])} for workspace in results]
64
- context_update = Context(where=where_clause, query=context.query)
65
- logger.info("Return context -> %s", context_update)
66
-
67
- results = [WorkspaceModel(name=n, id=i, type=knowledge_graph_query.target_type) for n, i in results]
68
-
69
- return {"where": where_clause}
70
-
71
- # return results
72
-
73
-
74
- # return ToolResponse(results=results, context_update=context_update)
@@ -1 +0,0 @@
1
- """init module."""
@@ -1,20 +0,0 @@
1
- """Define Models for users."""
2
-
3
- from typing import Annotated
4
-
5
- from pydantic import BaseModel, Field
6
-
7
-
8
- class UserModel(BaseModel):
9
- """Defines Model for describing users in OTCS.
10
-
11
- To display the user data as a markdown table, use the `UserModel` class.
12
- """
13
-
14
- user_id: Annotated[int, Field(description="ID of the user")] = None
15
- login: Annotated[str, Field(description="Login name of the user")] = None
16
- first_name: Annotated[str, Field(description="First name of the user")] = None
17
- last_name: Annotated[str, Field(description="Last name of the user")] = None
18
- email: Annotated[str, Field(description="Email address of the user")] = None
19
- department: Annotated[str, Field(description="Department of the user")] = None
20
- business_phone: Annotated[str, Field(description="Business phone number of the user")] = None
@@ -1,65 +0,0 @@
1
- """Define router for User endpoints."""
2
-
3
- import logging
4
- from typing import Annotated
5
-
6
- from fastapi import APIRouter, Body, Depends, HTTPException, status
7
- from pyxecm.otcs import OTCS
8
-
9
- from pyxecm_api.common.functions import get_otcs_object_from_otcsticket
10
-
11
- from .models import UserModel
12
-
13
- router = APIRouter(prefix="/otcm_user_agent", tags=["csai agents"])
14
-
15
- logger = logging.getLogger("pyxecm_api.agents.otcm_user_agent")
16
-
17
-
18
- @router.post(
19
- "/user",
20
- summary="Find a user by name or user attributes",
21
- responses={
22
- 200: {"description": "User found"},
23
- 403: {"description": "Invalid credentials"},
24
- 400: {"description": "User not found"},
25
- },
26
- response_model=UserModel,
27
- response_description="Details about a user. Best presented as a table with each user property in a row.",
28
- )
29
- def otcm_user_agent_find_user(
30
- otcs: Annotated[OTCS, Depends(get_otcs_object_from_otcsticket)],
31
- user: Annotated[UserModel, Body()],
32
- ) -> UserModel | None:
33
- """Find a user by by its name or other attributes.
34
-
35
- The user ID, the user name (login), user first name, user last name and user email will be returned.
36
- When a user is returned, display the user data as a markdown table.
37
- """
38
-
39
- response = otcs.get_users(
40
- where_name=user.login,
41
- where_first_name=user.first_name,
42
- where_last_name=user.last_name,
43
- where_business_email=user.email,
44
- )
45
-
46
- if not response or not response["results"]:
47
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
48
-
49
- try:
50
- department_id = otcs.get_result_value(response=response, key="group_id")
51
- department = otcs.get_group(group_id=department_id) if department_id else None
52
- department_name = otcs.get_result_value(response=department, key="name") if department else None
53
- return UserModel(
54
- user_id=otcs.get_result_value(response=response, key="id"),
55
- login=otcs.get_result_value(response=response, key="name"),
56
- first_name=otcs.get_result_value(response=response, key="first_name"),
57
- last_name=otcs.get_result_value(response=response, key="last_name"),
58
- email=otcs.get_result_value(response=response, key="business_email"),
59
- department=department_name,
60
- )
61
- except Exception as e:
62
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") from e
63
-
64
-
65
- # end function definition
@@ -1 +0,0 @@
1
- """init module."""
@@ -1,40 +0,0 @@
1
- """Define Models for workspaces."""
2
-
3
- from typing import Annotated, Any
4
-
5
- from pydantic import BaseModel, Field
6
-
7
-
8
- class WorkspaceAttributeModel(BaseModel):
9
- """Model for a workspace attribute."""
10
-
11
- category: Annotated[str, Field(description="Category for the workspace attributes")]
12
- attribute: Annotated[str, Field(description="Name of the attribute")]
13
- value: Annotated[str, Field(description="Value of the attribute")]
14
- set: Annotated[str, Field(description="Optional attribute group or set name")]
15
-
16
-
17
- class WorkspaceModel(BaseModel):
18
- """Defines Model for describing workspaces in OTCM (Opentext Content Management).
19
-
20
- To display an instance of this model, please display the link.
21
- """
22
-
23
- id: Annotated[int, Field(description="ID of the workspace")] = None
24
- name: Annotated[str, Field(description="Name of the workspace")] = None
25
- type: Annotated[str, Field(description="Name of the workspace type")] = None
26
- description: Annotated[str, Field(description="Description of the workspace")] = None
27
-
28
- link: Annotated[str, Field(description="Link to the workspace, should be used when the instance is displayed")] = (
29
- None
30
- )
31
-
32
- # attributes: Annotated[
33
- # list[WorkspaceAttributeModel] | None,
34
- # Body(description="List of custom attributes associated with the workspace"),
35
- # ] = None
36
-
37
- def model_post_init(self, context: Any) -> None: # noqa: ARG002, ANN401
38
- """Post model initialization."""
39
- # this could also be done with `default_factory`:
40
- self.link = f"[{self.name}](/cs/cs/app/nodes/{self.id})"