ibm-watsonx-orchestrate 1.0.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.
Files changed (89) hide show
  1. ibm_watsonx_orchestrate/__init__.py +28 -0
  2. ibm_watsonx_orchestrate/agent_builder/__init__.py +0 -0
  3. ibm_watsonx_orchestrate/agent_builder/agents/__init__.py +5 -0
  4. ibm_watsonx_orchestrate/agent_builder/agents/agent.py +27 -0
  5. ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +28 -0
  6. ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +28 -0
  7. ibm_watsonx_orchestrate/agent_builder/agents/types.py +204 -0
  8. ibm_watsonx_orchestrate/agent_builder/connections/__init__.py +27 -0
  9. ibm_watsonx_orchestrate/agent_builder/connections/connections.py +123 -0
  10. ibm_watsonx_orchestrate/agent_builder/connections/types.py +260 -0
  11. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base.py +27 -0
  12. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +59 -0
  13. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +243 -0
  14. ibm_watsonx_orchestrate/agent_builder/tools/__init__.py +4 -0
  15. ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +36 -0
  16. ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +332 -0
  17. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +195 -0
  18. ibm_watsonx_orchestrate/agent_builder/tools/types.py +162 -0
  19. ibm_watsonx_orchestrate/agent_builder/utils/__init__.py +0 -0
  20. ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py +149 -0
  21. ibm_watsonx_orchestrate/cli/__init__.py +0 -0
  22. ibm_watsonx_orchestrate/cli/commands/__init__.py +0 -0
  23. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +192 -0
  24. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +660 -0
  25. ibm_watsonx_orchestrate/cli/commands/channels/channels_command.py +15 -0
  26. ibm_watsonx_orchestrate/cli/commands/channels/channels_controller.py +16 -0
  27. ibm_watsonx_orchestrate/cli/commands/channels/types.py +15 -0
  28. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_command.py +32 -0
  29. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +141 -0
  30. ibm_watsonx_orchestrate/cli/commands/chat/chat_command.py +43 -0
  31. ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +307 -0
  32. ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +517 -0
  33. ibm_watsonx_orchestrate/cli/commands/environment/environment_command.py +78 -0
  34. ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +189 -0
  35. ibm_watsonx_orchestrate/cli/commands/environment/types.py +9 -0
  36. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +79 -0
  37. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +201 -0
  38. ibm_watsonx_orchestrate/cli/commands/login/login_command.py +17 -0
  39. ibm_watsonx_orchestrate/cli/commands/models/models_command.py +128 -0
  40. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +623 -0
  41. ibm_watsonx_orchestrate/cli/commands/settings/__init__.py +0 -0
  42. ibm_watsonx_orchestrate/cli/commands/settings/observability/__init__.py +0 -0
  43. ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/__init__.py +0 -0
  44. ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/langfuse_command.py +175 -0
  45. ibm_watsonx_orchestrate/cli/commands/settings/observability/observability_command.py +11 -0
  46. ibm_watsonx_orchestrate/cli/commands/settings/settings_command.py +10 -0
  47. ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +85 -0
  48. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +564 -0
  49. ibm_watsonx_orchestrate/cli/commands/tools/types.py +10 -0
  50. ibm_watsonx_orchestrate/cli/config.py +226 -0
  51. ibm_watsonx_orchestrate/cli/main.py +32 -0
  52. ibm_watsonx_orchestrate/client/__init__.py +0 -0
  53. ibm_watsonx_orchestrate/client/agents/agent_client.py +46 -0
  54. ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +38 -0
  55. ibm_watsonx_orchestrate/client/agents/external_agent_client.py +38 -0
  56. ibm_watsonx_orchestrate/client/analytics/__init__.py +0 -0
  57. ibm_watsonx_orchestrate/client/analytics/llm/__init__.py +0 -0
  58. ibm_watsonx_orchestrate/client/analytics/llm/analytics_llm_client.py +50 -0
  59. ibm_watsonx_orchestrate/client/base_api_client.py +113 -0
  60. ibm_watsonx_orchestrate/client/base_service_instance.py +10 -0
  61. ibm_watsonx_orchestrate/client/client.py +71 -0
  62. ibm_watsonx_orchestrate/client/client_errors.py +359 -0
  63. ibm_watsonx_orchestrate/client/connections/__init__.py +10 -0
  64. ibm_watsonx_orchestrate/client/connections/connections_client.py +162 -0
  65. ibm_watsonx_orchestrate/client/connections/utils.py +27 -0
  66. ibm_watsonx_orchestrate/client/credentials.py +123 -0
  67. ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +46 -0
  68. ibm_watsonx_orchestrate/client/local_service_instance.py +91 -0
  69. ibm_watsonx_orchestrate/client/service_instance.py +73 -0
  70. ibm_watsonx_orchestrate/client/tools/tool_client.py +41 -0
  71. ibm_watsonx_orchestrate/client/utils.py +95 -0
  72. ibm_watsonx_orchestrate/docker/compose-lite.yml +595 -0
  73. ibm_watsonx_orchestrate/docker/default.env +125 -0
  74. ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0-py3-none-any.whl +0 -0
  75. ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0.tar.gz +0 -0
  76. ibm_watsonx_orchestrate/docker/start-up.sh +61 -0
  77. ibm_watsonx_orchestrate/docker/tempus/common-config.yaml +1 -0
  78. ibm_watsonx_orchestrate/run/__init__.py +0 -0
  79. ibm_watsonx_orchestrate/run/connections.py +40 -0
  80. ibm_watsonx_orchestrate/utils/__init__.py +0 -0
  81. ibm_watsonx_orchestrate/utils/logging/__init__.py +0 -0
  82. ibm_watsonx_orchestrate/utils/logging/logger.py +26 -0
  83. ibm_watsonx_orchestrate/utils/logging/logging.yaml +18 -0
  84. ibm_watsonx_orchestrate/utils/utils.py +15 -0
  85. ibm_watsonx_orchestrate-1.0.0.dist-info/METADATA +34 -0
  86. ibm_watsonx_orchestrate-1.0.0.dist-info/RECORD +89 -0
  87. ibm_watsonx_orchestrate-1.0.0.dist-info/WHEEL +4 -0
  88. ibm_watsonx_orchestrate-1.0.0.dist-info/entry_points.txt +2 -0
  89. ibm_watsonx_orchestrate-1.0.0.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,46 @@
1
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient
2
+ import json
3
+ from typing_extensions import List
4
+ from ibm_watsonx_orchestrate.client.base_api_client import ClientAPIException
5
+
6
+
7
+ class KnowledgeBaseClient(BaseAPIClient):
8
+ """
9
+ Client to handle CRUD operations for Native Knowledge Base endpoint
10
+ """
11
+
12
+ def create(self, payload: dict) -> dict:
13
+ return self._post_form_data("/api/v1/orchestrate/knowledge-bases/documents", data={ "knowledge_base" : json.dumps(payload) })
14
+
15
+ def create_built_in(self, payload: dict, files: list) -> dict:
16
+ return self._post_form_data("/api/v1/orchestrate/knowledge-bases/documents", data={ "knowledge_base" : json.dumps(payload) }, files=files)
17
+
18
+ def get(self) -> dict:
19
+ return self._get("/api/v1/orchestrate/knowledge-bases")
20
+
21
+ def get_by_name(self, name: str) -> List[dict]:
22
+ kbs = self.get_by_names([name])
23
+ return None if len(kbs) == 0 else kbs[0]
24
+
25
+ def get_by_id(self, knowledge_base_id: str) -> dict:
26
+ return self._get(f"/api/v1/orchestrate/knowledge-bases/{knowledge_base_id}")
27
+
28
+ def get_by_names(self, name: List[str]) -> List[dict]:
29
+ formatted_names = [f"names={x}" for x in name]
30
+ return self._get(f"/api/v1/orchestrate/knowledge-bases?{'&'.join(formatted_names)}")
31
+
32
+ def status(self, knowledge_base_id: str) -> dict:
33
+ return self._get(f"/api/v1/orchestrate/knowledge-bases/{knowledge_base_id}/status")
34
+
35
+ def update(self, knowledge_base_id: str, payload: dict) -> dict:
36
+ return self._patch_form_data(f"/api/v1/orchestrate/knowledge-bases/{knowledge_base_id}/documents", data={ "knowledge_base" : json.dumps(payload) })
37
+
38
+ def update_with_documents(self, knowledge_base_id: str, payload: dict, files: list) -> dict:
39
+ return self._patch_form_data(f"/api/v1/orchestrate/knowledge-bases/{knowledge_base_id}/documents", data={ "knowledge_base" : json.dumps(payload) }, files=files)
40
+
41
+ def delete(self, knowledge_base_id: str,) -> dict:
42
+ return self._delete(f"/api/v1/orchestrate/knowledge-bases/{knowledge_base_id}")
43
+
44
+
45
+
46
+
@@ -0,0 +1,91 @@
1
+ from ibm_watsonx_orchestrate.client.base_service_instance import BaseServiceInstance
2
+ import logging
3
+ import requests
4
+ from ibm_watsonx_orchestrate.client.credentials import Credentials
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ DEFAULT_TENANT = {
9
+ "name": "wxo-dev",
10
+ "title": "WatsonX Orchestrate Development",
11
+ "tags": ["test"]
12
+ }
13
+
14
+ DEFAULT_USER = {"username": "wxo.archer@ibm.com", "password": "watsonx"}
15
+ DEFAULT_LOCAL_SERVICE_URL = "http://localhost:4321"
16
+ DEFAULT_LOCAL_AUTH_ENDPOINT = f"{DEFAULT_LOCAL_SERVICE_URL}/api/v1/auth/token"
17
+ DEFAULT_LOCAL_TENANT_URL = f"{DEFAULT_LOCAL_SERVICE_URL}/tenants"
18
+ DEFAULT_LOCAL_TENANT_AUTH_ENDPOINT = "{}/api/v1/auth/token?tenant_id={}"
19
+
20
+
21
+ class LocalServiceInstance(BaseServiceInstance):
22
+ """lite service instance for local development"""
23
+
24
+ def __init__(self, client) -> None:
25
+
26
+ self._logger = logging.getLogger(__name__)
27
+ self._client = client
28
+ self._credentials: Credentials = client.credentials
29
+ self._credentials.local_global_token = self._get_user_auth_token()
30
+
31
+ self.tenant_id = self._create_default_tenant_if_not_exist()
32
+ self.tenant_access_token = self._get_tenant_token(self.tenant_id)
33
+ # the local token does not have exp claim.
34
+ self._client.token = self.tenant_access_token
35
+ super().__init__()
36
+
37
+ @staticmethod
38
+ def get_default_tenant(apikey):
39
+ headers = {"Authorization": f"Bearer {apikey}",
40
+ "Content-Type": "application/json"}
41
+ resp = requests.get(DEFAULT_LOCAL_TENANT_URL, headers=headers)
42
+ if resp.status_code == 200:
43
+ tenant_config = resp.json()
44
+ for tenant in tenant_config:
45
+ if tenant["name"] == DEFAULT_TENANT["name"]:
46
+ return tenant
47
+ return {}
48
+ else:
49
+ resp.raise_for_status()
50
+
51
+ @staticmethod
52
+ def create_default_tenant(apikey):
53
+ headers = {"Authorization": f"Bearer {apikey}",
54
+ "Content-Type": "application/json"}
55
+ resp = requests.post(DEFAULT_LOCAL_TENANT_URL, headers=headers, json=DEFAULT_TENANT)
56
+ if resp.status_code == 201:
57
+ return True
58
+ else:
59
+ resp.raise_for_status()
60
+
61
+ def _create_default_tenant_if_not_exist(self) -> str:
62
+ user_auth_token = self._credentials.local_global_token
63
+ default_tenant = self.get_default_tenant(user_auth_token)
64
+
65
+ if not default_tenant:
66
+ logger.info("no local tenant found. A default tenant is created")
67
+ self.create_default_tenant(user_auth_token)
68
+ default_tenant = self.get_default_tenant(user_auth_token)
69
+ else:
70
+ logger.info("local tenant found")
71
+ tenant_id = default_tenant["id"]
72
+ return tenant_id
73
+
74
+ def _get_user_auth_token(self):
75
+ resp = requests.post(DEFAULT_LOCAL_AUTH_ENDPOINT, data=DEFAULT_USER)
76
+ if resp.status_code == 200:
77
+ return resp.json()["access_token"]
78
+ else:
79
+ resp.raise_for_status()
80
+
81
+ def _get_tenant_token(self, tenant_id: str):
82
+ resp = requests.post(DEFAULT_LOCAL_TENANT_AUTH_ENDPOINT.format(DEFAULT_LOCAL_SERVICE_URL, tenant_id),
83
+ data=DEFAULT_USER)
84
+ if resp.status_code == 200:
85
+ return resp.json()["access_token"]
86
+ else:
87
+ resp.raise_for_status()
88
+
89
+ def _create_token(self) -> str:
90
+
91
+ return self._get_tenant_token(self.tenant_id)
@@ -0,0 +1,73 @@
1
+ # -----------------------------------------------------------------------------------------
2
+ # (C) Copyright IBM Corp. 2024.
3
+ # https://opensource.org/licenses/BSD-3-Clause
4
+ # -----------------------------------------------------------------------------------------
5
+
6
+ from __future__ import annotations
7
+
8
+ from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
9
+ from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
10
+
11
+ from ibm_watsonx_orchestrate.client.utils import check_token_validity
12
+ from ibm_watsonx_orchestrate.client.base_service_instance import BaseServiceInstance
13
+ from ibm_watsonx_orchestrate.cli.commands.environment.types import EnvironmentAuthType
14
+
15
+ from ibm_watsonx_orchestrate.client.client_errors import (
16
+ ClientError,
17
+ )
18
+
19
+
20
+ class ServiceInstance(BaseServiceInstance):
21
+ """Connect, get details, and check usage of a Watson Machine Learning service instance."""
22
+
23
+ def __init__(self, client) -> None:
24
+ super().__init__()
25
+ self._client = client
26
+ self._credentials = client.credentials
27
+ self._client.token = self._get_token()
28
+
29
+ def _get_token(self) -> str:
30
+ # If no token is set
31
+ if self._client.token is None:
32
+ return self._create_token()
33
+
34
+ # Refresh is possible and token is expired
35
+ if self._is_token_refresh_possible() and self._check_token_expiry():
36
+ return self._create_token()
37
+
38
+ return self._client.token
39
+
40
+ def _create_token(self) -> str:
41
+ if not self._credentials.auth_type:
42
+ if ".cloud.ibm.com" in self._credentials.url:
43
+ return self._authenticate(EnvironmentAuthType.IBM_CLOUD_IAM)
44
+ else:
45
+ return self._authenticate(EnvironmentAuthType.MCSP)
46
+ else:
47
+ return self._authenticate(self._credentials.auth_type)
48
+
49
+ def _authenticate(self, auth_type: str) -> str:
50
+ """Handles authentication based on the auth_type."""
51
+ try:
52
+ match auth_type:
53
+ case EnvironmentAuthType.MCSP:
54
+ authenticator = MCSPAuthenticator(apikey=self._credentials.api_key, url=self._credentials.iam_url)
55
+ case EnvironmentAuthType.IBM_CLOUD_IAM:
56
+ authenticator = IAMAuthenticator(apikey=self._credentials.api_key, url=self._credentials.iam_url)
57
+ case _:
58
+ raise ClientError(f"Unsupported authentication type: {auth_type}")
59
+
60
+ return authenticator.token_manager.get_token()
61
+ except Exception as e:
62
+ raise ClientError(f"Error getting {auth_type.upper()} Token", e)
63
+
64
+
65
+ def _is_token_refresh_possible(self) -> bool:
66
+ if self._credentials.api_key:
67
+ return True
68
+ return False
69
+
70
+ def _check_token_expiry(self):
71
+ token = self._client.token
72
+
73
+ return not check_token_validity(token)
@@ -0,0 +1,41 @@
1
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
2
+ from typing_extensions import List
3
+
4
+ class ToolClient(BaseAPIClient):
5
+ """
6
+ Client to handle CRUD operations for Tool endpoint
7
+ """
8
+
9
+ def create(self, payload: dict) -> dict:
10
+ return self._post("/tools", data=payload)
11
+
12
+ def get(self) -> dict:
13
+ return self._get("/tools")
14
+
15
+ def update(self, agent_id: str, data: dict) -> dict:
16
+ return self._put(f"/tools/{agent_id}", data=data)
17
+
18
+ def delete(self, tool_id: str) -> dict:
19
+ return self._delete(f"/tools/{tool_id}")
20
+
21
+ def upload_tools_artifact(self, tool_id: str, file_path: str) -> dict:
22
+ return self._post(f"/tools/{tool_id}/upload", files={"file": (f"{tool_id}.zip", open(file_path, "rb"), "application/zip", {"Expires": "0"})})
23
+
24
+ def get_draft_by_name(self, tool_name: str) -> List[dict]:
25
+ return self.get_drafts_by_names([tool_name])
26
+
27
+ def get_drafts_by_names(self, tool_names: List[str]) -> List[dict]:
28
+ formatted_tool_names = [f"names={x}" for x in tool_names]
29
+ return self._get(f"/tools?{'&'.join(formatted_tool_names)}")
30
+
31
+ def get_draft_by_id(self, tool_id: str) -> List[dict]:
32
+ if tool_id is None:
33
+ return ""
34
+ else:
35
+ try:
36
+ tool = self._get(f"/tools/{tool_id}")
37
+ return tool
38
+ except ClientAPIException as e:
39
+ if e.response.status_code == 404 and "not found with the given name" in e.response.text:
40
+ return ""
41
+ raise(e)
@@ -0,0 +1,95 @@
1
+ from ibm_watsonx_orchestrate.cli.config import (
2
+ Config,
3
+ DEFAULT_CONFIG_FILE_FOLDER,
4
+ DEFAULT_CONFIG_FILE,
5
+ AUTH_CONFIG_FILE_FOLDER,
6
+ AUTH_CONFIG_FILE,
7
+ AUTH_SECTION_HEADER,
8
+ AUTH_MCSP_TOKEN_OPT,
9
+ CONTEXT_SECTION_HEADER,
10
+ CONTEXT_ACTIVE_ENV_OPT,
11
+ ENVIRONMENTS_SECTION_HEADER,
12
+ ENV_WXO_URL_OPT
13
+ )
14
+ from threading import Lock
15
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient
16
+ from ibm_watsonx_orchestrate.utils.utils import yaml_safe_load
17
+ import logging
18
+ from typing import TypeVar
19
+ import os
20
+ import jwt
21
+ import time
22
+
23
+ logger = logging.getLogger(__name__)
24
+ LOCK = Lock()
25
+ T = TypeVar("T", bound=BaseAPIClient)
26
+
27
+
28
+ def is_local_dev(url: str | None = None) -> bool:
29
+ if url is None:
30
+ cfg = Config()
31
+ active_env = cfg.read(CONTEXT_SECTION_HEADER, CONTEXT_ACTIVE_ENV_OPT)
32
+ url = cfg.get(ENVIRONMENTS_SECTION_HEADER, active_env, ENV_WXO_URL_OPT)
33
+
34
+ if url.startswith("http://localhost"):
35
+ return True
36
+
37
+ if url.startswith("http://127.0.0.1"):
38
+ return True
39
+
40
+ if url.startswith("http://[::1]"):
41
+ return True
42
+
43
+ if url.startswith("http://0.0.0.0"):
44
+ return True
45
+
46
+ return False
47
+
48
+ def check_token_validity(token: str) -> bool:
49
+ try:
50
+ token_claimset = jwt.decode(token, options={"verify_signature": False})
51
+ expiry = token_claimset.get('exp')
52
+
53
+ current_timestamp = int(time.time())
54
+ # Check if the token is not expired (or will not be expired in 10 minutes)
55
+ if not expiry or current_timestamp < expiry - 600:
56
+ return True
57
+ return False
58
+ except:
59
+ return False
60
+
61
+
62
+ def instantiate_client(client: type[T] , url: str | None=None) -> T:
63
+ try:
64
+ with LOCK:
65
+ with open(os.path.join(DEFAULT_CONFIG_FILE_FOLDER, DEFAULT_CONFIG_FILE), "r") as f:
66
+ config = yaml_safe_load(f)
67
+ active_env = config.get(CONTEXT_SECTION_HEADER, {}).get(CONTEXT_ACTIVE_ENV_OPT)
68
+
69
+ if not url:
70
+ url = config.get(ENVIRONMENTS_SECTION_HEADER, {}).get(active_env, {}).get(ENV_WXO_URL_OPT)
71
+
72
+ with open(os.path.join(AUTH_CONFIG_FILE_FOLDER, AUTH_CONFIG_FILE), "r") as f:
73
+ auth_config = yaml_safe_load(f)
74
+ auth_settings = auth_config.get(AUTH_SECTION_HEADER, {}).get(active_env, {})
75
+
76
+ if not active_env:
77
+ logger.error("No active environment set. Use `orchestrate env activate` to activate an environment")
78
+ exit(1)
79
+ if not url:
80
+ logger.error(f"No URL found for environment '{active_env}'. Use `orchestrate env list` to view existing environments and `orchesrtate env add` to reset the URL")
81
+ exit(1)
82
+ if not auth_settings:
83
+ logger.error(f"No credentials found for active env '{active_env}'. Use `orchestrate env activate {active_env}` to refresh your credentials")
84
+ exit(1)
85
+ token = auth_settings.get(AUTH_MCSP_TOKEN_OPT)
86
+ if not check_token_validity(token):
87
+ logger.error(f"The token found for environment '{active_env}' is missing or expired. Use `orchestrate env activate {active_env}` to fetch a new one")
88
+ exit(1)
89
+ client_instance = client(base_url=url, api_key=token, is_local=is_local_dev(url))
90
+
91
+ return client_instance
92
+ except FileNotFoundError as e:
93
+ message = "No active environment found. Please run `orchestrate env activate` to activate an environment"
94
+ logger.error(message)
95
+ raise FileNotFoundError(message)