ibm-watsonx-orchestrate 1.2.0__py3-none-any.whl → 1.4.2__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 (59) hide show
  1. ibm_watsonx_orchestrate/__init__.py +1 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/types.py +6 -1
  3. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base.py +16 -3
  4. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +4 -20
  5. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +13 -15
  6. ibm_watsonx_orchestrate/agent_builder/toolkits/base_toolkit.py +32 -0
  7. ibm_watsonx_orchestrate/agent_builder/toolkits/types.py +42 -0
  8. ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +14 -13
  9. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +4 -2
  10. ibm_watsonx_orchestrate/agent_builder/tools/types.py +2 -1
  11. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +29 -0
  12. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +273 -12
  13. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +2 -2
  14. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +79 -39
  15. ibm_watsonx_orchestrate/cli/commands/models/env_file_model_provider_mapper.py +180 -0
  16. ibm_watsonx_orchestrate/cli/commands/models/models_command.py +194 -8
  17. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +117 -48
  18. ibm_watsonx_orchestrate/cli/commands/server/types.py +105 -0
  19. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +55 -7
  20. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +123 -42
  21. ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +22 -1
  22. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +209 -25
  23. ibm_watsonx_orchestrate/cli/init_helper.py +43 -0
  24. ibm_watsonx_orchestrate/cli/main.py +3 -1
  25. ibm_watsonx_orchestrate/client/agents/agent_client.py +4 -1
  26. ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +5 -1
  27. ibm_watsonx_orchestrate/client/agents/external_agent_client.py +5 -1
  28. ibm_watsonx_orchestrate/client/analytics/llm/analytics_llm_client.py +2 -6
  29. ibm_watsonx_orchestrate/client/base_api_client.py +5 -2
  30. ibm_watsonx_orchestrate/client/connections/connections_client.py +15 -21
  31. ibm_watsonx_orchestrate/client/model_policies/__init__.py +0 -0
  32. ibm_watsonx_orchestrate/client/model_policies/model_policies_client.py +47 -0
  33. ibm_watsonx_orchestrate/client/model_policies/types.py +36 -0
  34. ibm_watsonx_orchestrate/client/models/__init__.py +0 -0
  35. ibm_watsonx_orchestrate/client/models/models_client.py +46 -0
  36. ibm_watsonx_orchestrate/client/models/types.py +177 -0
  37. ibm_watsonx_orchestrate/client/toolkit/toolkit_client.py +15 -6
  38. ibm_watsonx_orchestrate/client/tools/tempus_client.py +40 -0
  39. ibm_watsonx_orchestrate/client/tools/tool_client.py +8 -0
  40. ibm_watsonx_orchestrate/docker/compose-lite.yml +68 -13
  41. ibm_watsonx_orchestrate/docker/default.env +22 -12
  42. ibm_watsonx_orchestrate/docker/tempus/common-config.yaml +1 -1
  43. ibm_watsonx_orchestrate/experimental/flow_builder/__init__.py +0 -0
  44. ibm_watsonx_orchestrate/experimental/flow_builder/flows/__init__.py +41 -0
  45. ibm_watsonx_orchestrate/experimental/flow_builder/flows/constants.py +17 -0
  46. ibm_watsonx_orchestrate/experimental/flow_builder/flows/data_map.py +91 -0
  47. ibm_watsonx_orchestrate/experimental/flow_builder/flows/decorators.py +143 -0
  48. ibm_watsonx_orchestrate/experimental/flow_builder/flows/events.py +72 -0
  49. ibm_watsonx_orchestrate/experimental/flow_builder/flows/flow.py +1288 -0
  50. ibm_watsonx_orchestrate/experimental/flow_builder/node.py +97 -0
  51. ibm_watsonx_orchestrate/experimental/flow_builder/resources/flow_status.openapi.yml +98 -0
  52. ibm_watsonx_orchestrate/experimental/flow_builder/types.py +492 -0
  53. ibm_watsonx_orchestrate/experimental/flow_builder/utils.py +113 -0
  54. ibm_watsonx_orchestrate/utils/utils.py +5 -2
  55. {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/METADATA +6 -2
  56. {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/RECORD +59 -36
  57. {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/WHEEL +0 -0
  58. {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/entry_points.txt +0 -0
  59. {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,43 @@
1
+ import importlib.metadata
2
+ from importlib import resources
3
+ from typing import Optional
4
+ from rich import print as pprint
5
+ from dotenv import dotenv_values
6
+ import typer
7
+
8
+ from ibm_watsonx_orchestrate.cli.config import Config, PYTHON_REGISTRY_HEADER, \
9
+ PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT
10
+
11
+
12
+ def version_callback(checkVersion: bool=True):
13
+ if checkVersion:
14
+ __version__ = importlib.metadata.version('ibm-watsonx-orchestrate')
15
+ default_env = dotenv_values(resources.files("ibm_watsonx_orchestrate.docker").joinpath("default.env"))
16
+ cfg = Config()
17
+ pypi_override = cfg.read(PYTHON_REGISTRY_HEADER, PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT)
18
+
19
+ adk_version_str = f"[bold]ADK Version[/bold]: {__version__}"
20
+ if pypi_override is not None:
21
+ adk_version_str += f" [red bold](override: {pypi_override})[/red bold]"
22
+ pprint(adk_version_str)
23
+
24
+
25
+ pprint("[bold]Developer Edition Image Tags[/bold] [italic](if not overridden in env file)[/italic]")
26
+ for key, value in default_env.items():
27
+ if key.endswith('_TAG') or key == 'DBTAG':
28
+ pprint(f" [bold]{key}[/bold]: {value}")
29
+
30
+ raise typer.Exit()
31
+
32
+
33
+
34
+ def init_callback(
35
+ ctx: typer.Context,
36
+ version: Optional[bool] = typer.Option(
37
+ None,
38
+ "--version",
39
+ help="Show the installed version of the ADK and Developer Edition Tags",
40
+ callback=version_callback
41
+ )
42
+ ):
43
+ pass
@@ -12,10 +12,12 @@ from ibm_watsonx_orchestrate.cli.commands.environment.environment_command import
12
12
  from ibm_watsonx_orchestrate.cli.commands.channels.channels_command import channel_app
13
13
  from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_command import knowledge_bases_app
14
14
  from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_command import toolkits_app
15
+ from ibm_watsonx_orchestrate.cli.init_helper import init_callback
15
16
 
16
17
  app = typer.Typer(
17
18
  no_args_is_help=True,
18
- pretty_exceptions_enable=False
19
+ pretty_exceptions_enable=False,
20
+ callback=init_callback
19
21
  )
20
22
  app.add_typer(login_app)
21
23
  app.add_typer(environment_app, name="env", help='Add, remove, or select the activate env other commands will interact with (either your local server or a production instance)')
@@ -42,5 +42,8 @@ class AgentClient(BaseAPIClient):
42
42
  if e.response.status_code == 404 and "not found with the given name" in e.response.text:
43
43
  return ""
44
44
  raise(e)
45
-
45
+
46
+ def get_drafts_by_ids(self, agent_ids: List[str]) -> List[dict]:
47
+ formatted_agent_ids = [f"ids={x}" for x in agent_ids]
48
+ return self._get(f"{self.base_endpoint}?{'&'.join(formatted_agent_ids)}")
46
49
 
@@ -25,7 +25,7 @@ class AssistantAgentClient(BaseAPIClient):
25
25
  formatted_agent_names = [f"names={x}" for x in agent_names]
26
26
  return self._get(f"/assistants/watsonx?{'&'.join(formatted_agent_names)}")
27
27
 
28
- def get_draft_by_id(self, agent_id: str) -> List[dict]:
28
+ def get_draft_by_id(self, agent_id: str) -> dict | str:
29
29
  if agent_id is None:
30
30
  return ""
31
31
  else:
@@ -36,3 +36,7 @@ class AssistantAgentClient(BaseAPIClient):
36
36
  if e.response.status_code == 404 and "Assistant not found" in e.response.text:
37
37
  return ""
38
38
  raise(e)
39
+
40
+ def get_drafts_by_ids(self, agent_ids: List[str]) -> List[dict]:
41
+ formatted_agent_ids = [f"ids={x}" for x in agent_ids]
42
+ return self._get(f"/assistants/watsonx?{'&'.join(formatted_agent_ids)}")
@@ -33,6 +33,10 @@ class ExternalAgentClient(BaseAPIClient):
33
33
  agent = self._get(f"/agents/external-chat/{agent_id}")
34
34
  return agent
35
35
  except ClientAPIException as e:
36
- if e.response.status_code == 404 and "not found with the given name" in e.response.text:
36
+ if e.response.status_code == 404 and ("not found with the given name" in e.response.text or "Assistant not found" in e.response.text):
37
37
  return ""
38
38
  raise(e)
39
+
40
+ def get_drafts_by_ids(self, agent_ids: List[str]) -> List[dict]:
41
+ formatted_agent_ids = [f"ids={x}" for x in agent_ids]
42
+ return self._get(f"/agents/external-chat?{'&'.join(formatted_agent_ids)}")
@@ -32,12 +32,8 @@ class AnalyticsLLMClient(BaseAPIClient):
32
32
  def create(self):
33
33
  raise RuntimeError('unimplemented')
34
34
 
35
- def get(self, project_id: Optional[str] = None) -> AnalyticsLLMConfig:
36
- params = {}
37
- if project_id:
38
- params['project_id'] = project_id
39
-
40
- response = self._get(f"/analytics/llm", params=params)
35
+ def get(self) -> AnalyticsLLMConfig:
36
+ response = self._get(f"/analytics/llm")
41
37
 
42
38
  return AnalyticsLLMConfig.model_validate(response)
43
39
 
@@ -45,12 +45,15 @@ class BaseAPIClient:
45
45
  headers["Authorization"] = f"Bearer {self.authenticator.token_manager.get_token()}"
46
46
  return headers
47
47
 
48
- def _get(self, path: str, params: dict = None, data=None) -> dict:
48
+ def _get(self, path: str, params: dict = None, data=None, return_raw=False) -> dict:
49
49
 
50
50
  url = f"{self.base_url}{path}"
51
51
  response = requests.get(url, headers=self._get_headers(), params=params, data=data)
52
52
  self._check_response(response)
53
- return response.json()
53
+ if not return_raw:
54
+ return response.json()
55
+ else:
56
+ return response
54
57
 
55
58
  def _post(self, path: str, data: dict = None, files: dict = None) -> dict:
56
59
  url = f"{self.base_url}{path}"
@@ -51,7 +51,7 @@ class ConnectionsClient(BaseAPIClient):
51
51
  # DELETE api/v1/connections/applications/{app_id}
52
52
  def delete(self, app_id: str) -> dict:
53
53
  return self._delete(f"/connections/applications/{app_id}")
54
-
54
+
55
55
  # GET /api/v1/connections/applications/{app_id}
56
56
  def get(self, app_id: str) -> GetConnectionResponse:
57
57
  try:
@@ -60,7 +60,7 @@ class ConnectionsClient(BaseAPIClient):
60
60
  if e.response.status_code == 404:
61
61
  return None
62
62
  raise e
63
-
63
+
64
64
 
65
65
  # GET api/v1/connections/applications
66
66
  def list(self) -> List[ListConfigsResponse]:
@@ -75,15 +75,15 @@ class ConnectionsClient(BaseAPIClient):
75
75
  return []
76
76
  raise e
77
77
 
78
-
78
+
79
79
  # POST /api/v1/connections/applications/{app_id}/configurations
80
80
  def create_config(self, app_id: str, payload: dict) -> None:
81
81
  self._post(f"/connections/applications/{app_id}/configurations", data=payload)
82
-
82
+
83
83
  # PATCH /api/v1/connections/applications/{app_id}/configurations/{env}
84
84
  def update_config(self, app_id: str, env: ConnectionEnvironment, payload: dict) -> None:
85
85
  self._patch(f"/connections/applications/{app_id}/configurations/{env}", data=payload)
86
-
86
+
87
87
  # `GET /api/v1/connections/applications/{app_id}/configurations/{env}'
88
88
  def get_config(self, app_id: str, env: ConnectionEnvironment) -> GetConfigResponse:
89
89
  try:
@@ -93,7 +93,7 @@ class ConnectionsClient(BaseAPIClient):
93
93
  if e.response.status_code == 404:
94
94
  return None
95
95
  raise e
96
-
96
+
97
97
  # POST /api/v1/connections/applications/{app_id}/configs/{env}/credentials
98
98
  # POST /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
99
99
  def create_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_sso: bool) -> None:
@@ -101,7 +101,7 @@ class ConnectionsClient(BaseAPIClient):
101
101
  self._post(f"/connections/applications/{app_id}/configs/{env}/credentials", data=payload)
102
102
  else:
103
103
  self._post(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials", data=payload)
104
-
104
+
105
105
  # PATCH /api/v1/connections/applications/{app_id}/configs/{env}/credentials
106
106
  # PATCH /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
107
107
  def update_credentials(self, app_id: str, env: ConnectionEnvironment, payload: dict, use_sso: bool) -> None:
@@ -109,7 +109,7 @@ class ConnectionsClient(BaseAPIClient):
109
109
  self._patch(f"/connections/applications/{app_id}/configs/{env}/credentials", data=payload)
110
110
  else:
111
111
  self._patch(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials", data=payload)
112
-
112
+
113
113
  # GET /api/v1/connections/applications/{app_id}/configs/credentials?env={env}
114
114
  # GET /api/v1/connections/applications/{app_id}/configs/runtime_credentials?env={env}
115
115
  def get_credentials(self, app_id: str, env: ConnectionEnvironment, use_sso: bool) -> dict:
@@ -122,7 +122,7 @@ class ConnectionsClient(BaseAPIClient):
122
122
  if e.response.status_code == 404:
123
123
  return None
124
124
  raise e
125
-
125
+
126
126
  # DELETE /api/v1/connections/applications/{app_id}/configs/{env}/credentials
127
127
  # DELETE /api/v1/connections/applications/{app_id}/configs/{env}/runtime_credentials
128
128
  def delete_credentials(self, app_id: str, env: ConnectionEnvironment, use_sso: bool) -> None:
@@ -130,7 +130,7 @@ class ConnectionsClient(BaseAPIClient):
130
130
  self._delete(f"/connections/applications/{app_id}/configs/{env}/credentials")
131
131
  else:
132
132
  self._delete(f"/connections/applications/{app_id}/configs/{env}/runtime_credentials")
133
-
133
+
134
134
  def get_draft_by_app_id(self, app_id: str) -> GetConnectionResponse:
135
135
  return self.get(app_id=app_id)
136
136
 
@@ -141,22 +141,16 @@ class ConnectionsClient(BaseAPIClient):
141
141
  if connection:
142
142
  connections += connection
143
143
  return connections
144
-
144
+
145
145
  def get_draft_by_id(self, conn_id) -> str:
146
146
  """Retrieve the app ID for a given connection ID."""
147
147
  if conn_id is None:
148
148
  return ""
149
149
  try:
150
- connections = self.list()
150
+ app_details = self._get(f"/connections/applications/id/{conn_id}")
151
+ return app_details.get("app_id")
151
152
  except ClientAPIException as e:
152
153
  if e.response.status_code == 404:
153
154
  logger.warning(f"Connections not found. Returning connection ID: {conn_id}")
154
- return conn_id
155
- raise
156
-
157
- app_id = next((conn.app_id for conn in connections if conn.connection_id == conn_id), None)
158
-
159
- if app_id is None:
160
- logger.warning(f"Connection with ID {conn_id} not found. Returning connection ID.")
161
- return conn_id
162
- return app_id
155
+ return conn_id
156
+ raise e
@@ -0,0 +1,47 @@
1
+ from typing import List
2
+
3
+ from pydantic import ValidationError
4
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
5
+
6
+ import logging
7
+
8
+ from ibm_watsonx_orchestrate.client.model_policies.types import ModelPolicy
9
+ from ibm_watsonx_orchestrate.client.models.types import ListVirtualModel, CreateVirtualModel
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+
15
+
16
+
17
+ class ModelPoliciesClient(BaseAPIClient):
18
+ """
19
+ Client to handle CRUD operations for ModelPolicies endpoint
20
+ """
21
+ # POST api/v1/model_policy
22
+ def create(self, model: ModelPolicy) -> None:
23
+ self._post("/model_policy", data=model.model_dump())
24
+
25
+ # DELETE api/v1/model_policy/{model_policy_id}
26
+ def delete(self, model_policy_id: str) -> dict:
27
+ return self._delete(f"/model_policy/{model_policy_id}")
28
+
29
+ # GET /api/v1/model_policy/{app_id}
30
+ def get(self, model_policy_id: str):
31
+ raise NotImplementedError
32
+
33
+
34
+ # GET api/v1/model_policy
35
+ def list(self) -> List[ModelPolicy]:
36
+ try:
37
+ res = self._get(f"/model_policy")
38
+ return [ModelPolicy.model_validate(policy) for policy in res]
39
+ except ValidationError as e:
40
+ logger.error("Received unexpected response from server")
41
+ raise e
42
+ except ClientAPIException as e:
43
+ if e.response.status_code == 404:
44
+ return []
45
+ raise e
46
+
47
+
@@ -0,0 +1,36 @@
1
+ from enum import Enum
2
+ from typing import Optional, List, Dict, Any, Union
3
+
4
+ from pydantic import Field, BaseModel, ConfigDict
5
+
6
+ class ModelPolicyStrategyMode(str, Enum):
7
+ LOAD_BALANCED = "loadbalance"
8
+ FALL_BACK = "fallback"
9
+ SINGLE = "single"
10
+
11
+ class ModelPolicyStrategy(BaseModel):
12
+ mode: ModelPolicyStrategyMode = None
13
+ on_status_codes: List[int] = None
14
+
15
+ class ModelPolicyRetry(BaseModel):
16
+ attempts: int = None
17
+ on_status_codes: List[int] = None
18
+
19
+ class ModelPolicyTarget(BaseModel):
20
+ model_id: str = None
21
+ weight: int = None
22
+
23
+
24
+ class ModelPolicyInner(BaseModel):
25
+ strategy: ModelPolicyStrategy = None
26
+ retry: ModelPolicyRetry = None
27
+ targets: List[Union[ModelPolicyTarget, 'ModelPolicyInner']] = None
28
+
29
+
30
+ class ModelPolicy(BaseModel):
31
+ model_config = ConfigDict(extra='allow')
32
+
33
+ name: str
34
+ display_name: str
35
+ policy: ModelPolicyInner
36
+
File without changes
@@ -0,0 +1,46 @@
1
+ from typing import List
2
+
3
+ from pydantic import ValidationError
4
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
5
+
6
+ import logging
7
+
8
+ from ibm_watsonx_orchestrate.client.models.types import ListVirtualModel, CreateVirtualModel
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+
14
+
15
+
16
+ class ModelsClient(BaseAPIClient):
17
+ """
18
+ Client to handle CRUD operations for Models endpoint
19
+ """
20
+ # POST api/v1/models
21
+ def create(self, model: CreateVirtualModel) -> None:
22
+ self._post("/models", data=model.model_dump())
23
+
24
+ # DELETE api/v1/models/{model_id}
25
+ def delete(self, model_id: str) -> dict:
26
+ return self._delete(f"/models/{model_id}")
27
+
28
+ # GET /api/v1/models/{app_id}
29
+ def get(self, model_id: str):
30
+ raise NotImplementedError
31
+
32
+
33
+ # GET api/v1/models
34
+ def list(self) -> List[ListVirtualModel]:
35
+ try:
36
+ res = self._get(f"/models")
37
+ return [ListVirtualModel.model_validate(conn) for conn in res]
38
+ except ValidationError as e:
39
+ logger.error("Received unexpected response from server")
40
+ raise e
41
+ except ClientAPIException as e:
42
+ if e.response.status_code == 404:
43
+ return []
44
+ raise e
45
+
46
+
@@ -0,0 +1,177 @@
1
+ from typing import Optional, List, Dict, Any, Union
2
+ from enum import Enum
3
+ from pydantic import Field, BaseModel, ConfigDict
4
+
5
+ class ModelProvider(str, Enum):
6
+ OPENAI = 'openai'
7
+ A21 = 'a21'
8
+ ANTHROPIC = 'anthropic'
9
+ ANYSCALE = 'anyscale'
10
+ AZURE_OPENAI = 'azure-openai'
11
+ AZURE_AI = 'azure-ai'
12
+ BEDROCK = 'bedrock'
13
+ CEREBRAS = 'cerebras'
14
+ COHERE = 'cohere'
15
+ GOOGLE = 'google'
16
+ VERTEX_AI = 'vertex-ai'
17
+ GROQ = 'groq'
18
+ HUGGINGFACE = 'huggingface'
19
+ MISTRAL_AI = 'mistral-ai'
20
+ JINA = 'jina'
21
+ OLLAMA = 'ollama'
22
+ OPENROUTER = 'openrouter'
23
+ STABILITY_AI = 'stability-ai'
24
+ TOGETHER_AI = 'together-ai'
25
+ WATSONX = 'watsonx'
26
+
27
+ def __str__(self):
28
+ return self.value
29
+
30
+ def __repr__(self):
31
+ return self.value
32
+
33
+
34
+ class ProviderConfig(BaseModel):
35
+ # Required fields
36
+ provider: Optional[str]=''
37
+
38
+
39
+ api_key: Optional[str] = '' # this is not optional
40
+ url_to_fetch: Optional[str] = Field(None, alias="urlToFetch")
41
+
42
+ # Misc
43
+ custom_host: Optional[str] = Field(None, alias="customHost")
44
+ forward_headers: Optional[List[str]] = Field(None, alias="forwardHeaders")
45
+ index: Optional[int] = None
46
+ cache: Optional[Union[str, Dict[str, Any]]] = None # Define CacheSettings if needed
47
+ metadata: Optional[Dict[str, str]] = None
48
+ request_timeout: Optional[int] = Field(None, alias="requestTimeout")
49
+ transform_to_form_data: Optional[bool] = Field(None, alias="transformToFormData")
50
+
51
+ # Azure specific
52
+ azure_resource_name: Optional[str] = Field(None, alias="resourceName")
53
+ azure_deployment_id: Optional[str] = Field(None, alias="deploymentId")
54
+ azure_api_version: Optional[str] = Field(None, alias="apiVersion")
55
+ ad_auth: Optional[str] = Field(None, alias="adAuth")
56
+ azure_auth_mode: Optional[str] = Field(None, alias="azureAuthMode")
57
+ azure_managed_client_id: Optional[str] = Field(None, alias="azureManagedClientId")
58
+ azure_entra_client_id: Optional[str] = Field(None, alias="azureEntraClientId")
59
+ azure_entra_client_secret: Optional[str] = Field(None, alias="azureEntraClientSecret")
60
+ azure_entra_tenant_id: Optional[str] = Field(None, alias="azureEntraTenantId")
61
+ azure_ad_token: Optional[str] = Field(None, alias="azureAdToken")
62
+ azure_model_name: Optional[str] = Field(None, alias="azureModelName")
63
+
64
+ # Workers AI specific
65
+ workers_ai_account_id: Optional[str] = Field(None, alias="workersAiAccountId")
66
+
67
+
68
+ # AWS
69
+ aws_secret_access_key: Optional[str] = Field(None, alias="awsSecretAccessKey")
70
+ aws_access_key_id: Optional[str] = Field(None, alias="awsAccessKeyId")
71
+ aws_session_token: Optional[str] = Field(None, alias="awsSessionToken")
72
+ aws_region: Optional[str] = Field(None, alias="awsRegion")
73
+ aws_auth_type: Optional[str] = Field(None, alias="awsAuthType")
74
+ aws_role_arn: Optional[str] = Field(None, alias="awsRoleArn")
75
+ aws_external_id: Optional[str] = Field(None, alias="awsExternalId")
76
+ aws_s3_bucket: Optional[str] = Field(None, alias="awsS3Bucket")
77
+ aws_s3_object_key: Optional[str] = Field(None, alias="awsS3ObjectKey")
78
+ aws_bedrock_model: Optional[str] = Field(None, alias="awsBedrockModel")
79
+ aws_server_side_encryption: Optional[str] = Field(None, alias="awsServerSideEncryption")
80
+ aws_server_side_encryption_kms_key_id: Optional[str] = Field(None, alias="awsServerSideEncryptionKMSKeyId")
81
+
82
+ # Sagemaker
83
+ amzn_sagemaker_custom_attributes: Optional[str] = Field(None, alias="amznSagemakerCustomAttributes")
84
+ amzn_sagemaker_target_model: Optional[str] = Field(None, alias="amznSagemakerTargetModel")
85
+ amzn_sagemaker_target_variant: Optional[str] = Field(None, alias="amznSagemakerTargetVariant")
86
+ amzn_sagemaker_target_container_hostname: Optional[str] = Field(None, alias="amznSagemakerTargetContainerHostname")
87
+ amzn_sagemaker_inference_id: Optional[str] = Field(None, alias="amznSagemakerInferenceId")
88
+ amzn_sagemaker_enable_explanations: Optional[str] = Field(None, alias="amznSagemakerEnableExplanations")
89
+ amzn_sagemaker_inference_component: Optional[str] = Field(None, alias="amznSagemakerInferenceComponent")
90
+ amzn_sagemaker_session_id: Optional[str] = Field(None, alias="amznSagemakerSessionId")
91
+ amzn_sagemaker_model_name: Optional[str] = Field(None, alias="amznSagemakerModelName")
92
+
93
+ # Stability AI
94
+ stability_client_id: Optional[str] = Field(None, alias="stabilityClientId")
95
+ stability_client_user_id: Optional[str] = Field(None, alias="stabilityClientUserId")
96
+ stability_client_version: Optional[str] = Field(None, alias="stabilityClientVersion")
97
+
98
+ # Hugging Face
99
+ huggingface_base_url: Optional[str] = Field(None, alias="huggingfaceBaseUrl")
100
+
101
+ # Google Vertex AI
102
+ vertex_region: Optional[str] = Field(None, alias="vertexRegion")
103
+ vertex_project_id: Optional[str] = Field(None, alias="vertexProjectId")
104
+ vertex_service_account_json: Optional[Dict[str, Any]] = Field(None, alias="vertexServiceAccountJson")
105
+ vertex_storage_bucket_name: Optional[str] = Field(None, alias="vertexStorageBucketName")
106
+ vertex_model_name: Optional[str] = Field(None, alias="vertexModelName")
107
+
108
+ filename: Optional[str] = None
109
+
110
+ after_request_hooks: Optional[List[Dict[str, Any]]] = Field(None, alias="afterRequestHooks")
111
+ before_request_hooks: Optional[List[Dict[str, Any]]] = Field(None, alias="beforeRequestHooks")
112
+ default_input_guardrails: Optional[List[Dict[str, Any]]] = Field(None, alias="defaultInputGuardrails")
113
+ default_output_guardrails: Optional[List[Dict[str, Any]]] = Field(None, alias="defaultOutputGuardrails")
114
+
115
+ # OpenAI
116
+ openai_project: Optional[str] = Field(None, alias="openaiProject")
117
+ openai_organization: Optional[str] = Field(None, alias="openaiOrganization")
118
+ openai_beta: Optional[str] = Field(None, alias="openaiBeta")
119
+
120
+ # Azure Inference
121
+ azure_deployment_name: Optional[str] = Field(None, alias="azureDeploymentName")
122
+ azure_api_version: Optional[str] = Field(None, alias="azureApiVersion")
123
+ azure_extra_params: Optional[str] = Field(None, alias="azureExtraParams")
124
+ azure_foundry_url: Optional[str] = Field(None, alias="azureFoundryUrl")
125
+
126
+ strict_open_ai_compliance: Optional[bool] = Field(False, alias="strictOpenAiCompliance")
127
+ mistral_fim_completion: Optional[str] = Field(None, alias="mistralFimCompletion")
128
+
129
+ # Anthropic
130
+ anthropic_beta: Optional[str] = Field(None, alias="anthropicBeta")
131
+ anthropic_version: Optional[str] = Field(None, alias="anthropicVersion")
132
+
133
+ # Fireworks
134
+ fireworks_account_id: Optional[str] = Field(None, alias="fireworksAccountId")
135
+
136
+ # Cortex
137
+ snowflake_account: Optional[str] = Field(None, alias="snowflakeAccount")
138
+
139
+ # WatsonX
140
+ watsonx_version: Optional[str] = Field(None, alias="watsonxVersion")
141
+ watsonx_space_id: Optional[str] = Field(None, alias="watsonxSpaceId")
142
+ watsonx_project_id: Optional[str] = Field(None, alias="watsonxProjectId")
143
+
144
+ model_config = {
145
+ "populate_by_name": True, # Replaces allow_population_by_field_name
146
+ "extra": "forbid", # Same as before
147
+ "json_schema_extra": lambda schema: schema.get("properties", {}).pop("provider", None)
148
+ }
149
+
150
+
151
+
152
+ class CreateVirtualModel(BaseModel):
153
+ model_config = ConfigDict(extra='allow')
154
+
155
+ name: str
156
+ display_name: Optional[str]
157
+ description: Optional[str]
158
+ config: Optional[dict] = None
159
+ provider_config: ProviderConfig
160
+ tags: List[str]
161
+ model_type: str = 'chat'
162
+
163
+
164
+ class ListVirtualModel(BaseModel):
165
+ model_config = ConfigDict(extra='allow')
166
+
167
+ id: Optional[str] = None
168
+ name: Optional[str] = None
169
+ display_name: Optional[str] = None
170
+ description: Optional[str] = None
171
+ provider: Optional[str] = None
172
+ # model_configuration: Optional[dict] = None
173
+ provider_config: Optional[ProviderConfig] = None
174
+ tags: Optional[List[str]] = None
175
+ model_type: Optional[str] = None
176
+
177
+ ANTHROPIC_DEFAULT_MAX_TOKENS = 4096
@@ -1,9 +1,18 @@
1
1
  from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
2
+ from ibm_watsonx_orchestrate.client.utils import is_local_dev
2
3
  from typing_extensions import List
3
4
  import os
4
5
  import json
5
6
 
6
7
  class ToolKitClient(BaseAPIClient):
8
+
9
+ def __init__(self, *args, **kwargs):
10
+ super().__init__(*args, **kwargs)
11
+ self.base_endpoint = "/orchestrate/toolkits" if is_local_dev(self.base_url) else "/toolkits"
12
+
13
+ def get(self) -> dict:
14
+ return self._get(self.base_endpoint)
15
+
7
16
  # POST /toolkits/prepare/list-tools
8
17
  def list_tools(self, zip_file_path: str, command: str, args: List[str]) -> List[str]:
9
18
  """
@@ -24,7 +33,7 @@ class ToolKitClient(BaseAPIClient):
24
33
  "file": (filename, f, "application/zip"),
25
34
  }
26
35
 
27
- response = self._post("/orchestrate/toolkits/prepare/list-tools", files=files)
36
+ response = self._post(f"{self.base_endpoint}/prepare/list-tools", files=files)
28
37
 
29
38
  return response.get("tools", [])
30
39
 
@@ -35,7 +44,7 @@ class ToolKitClient(BaseAPIClient):
35
44
  Creates new toolkit metadata
36
45
  """
37
46
  try:
38
- return self._post("/orchestrate/toolkits", data=payload)
47
+ return self._post(self.base_endpoint, data=payload)
39
48
  except ClientAPIException as e:
40
49
  if e.response.status_code == 400 and "already exists" in e.response.text:
41
50
  raise ClientAPIException(
@@ -54,25 +63,25 @@ class ToolKitClient(BaseAPIClient):
54
63
  files = {
55
64
  "file": (filename, f, "application/zip", {"Expires": "0"})
56
65
  }
57
- return self._post(f"/orchestrate/toolkits/{toolkit_id}/upload", files=files)
66
+ return self._post(f"{self.base_endpoint}/{toolkit_id}/upload", files=files)
58
67
 
59
68
  # DELETE /toolkits/{toolkit-id}
60
69
  def delete(self, toolkit_id: str) -> dict:
61
- return self._delete(f"/orchestrate/toolkits/{toolkit_id}")
70
+ return self._delete(f"{self.base_endpoint}/{toolkit_id}")
62
71
 
63
72
  def get_draft_by_name(self, toolkit_name: str) -> List[dict]:
64
73
  return self.get_drafts_by_names([toolkit_name])
65
74
 
66
75
  def get_drafts_by_names(self, toolkit_names: List[str]) -> List[dict]:
67
76
  formatted_toolkit_names = [f"names={x}" for x in toolkit_names]
68
- return self._get(f"/orchestrate/toolkits?{'&'.join(formatted_toolkit_names)}")
77
+ return self._get(f"{self.base_endpoint}?{'&'.join(formatted_toolkit_names)}")
69
78
 
70
79
  def get_draft_by_id(self, toolkit_id: str) -> dict:
71
80
  if toolkit_id is None:
72
81
  return ""
73
82
  else:
74
83
  try:
75
- toolkit = self._get(f"/orchestrate/toolkits/{toolkit_id}")
84
+ toolkit = self._get(f"{self.base_endpoint}/{toolkit_id}")
76
85
  return toolkit
77
86
  except ClientAPIException as e:
78
87
  if e.response.status_code == 404 and "not found with the given name" in e.response.text:
@@ -0,0 +1,40 @@
1
+ from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
2
+ from typing_extensions import List
3
+ from urllib.parse import urlparse, urlunparse
4
+ from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
5
+
6
+ DEFAULT_TEMPUS_PORT= 9044
7
+
8
+ class TempusClient(BaseAPIClient):
9
+ """
10
+ Client to handle CRUD operations for Tempus endpoint
11
+
12
+ This may be temporary and may want to create a proxy API in wxo-server
13
+ to redirect to the internal tempus runtime, and add a new operation in the ToolClient instead
14
+ """
15
+ def __init__(self, base_url: str, api_key: str = None, is_local: bool = False, authenticator: MCSPAuthenticator = None):
16
+ parsed_url = urlparse(base_url)
17
+
18
+ # Reconstruct netloc with new port - use default above - eventually we need to open up a way through the wxo-server API
19
+ new_netloc = f"{parsed_url.hostname}:{DEFAULT_TEMPUS_PORT}"
20
+
21
+ # Replace netloc and rebuild the URL
22
+ new_url = urlunparse(parsed_url._replace(netloc=new_netloc))
23
+ # remove trailing slash
24
+
25
+ super().__init__(
26
+ base_url=new_url,
27
+ api_key=api_key,
28
+ is_local=is_local,
29
+ authenticator=authenticator
30
+ )
31
+
32
+ def create_update_flow_model(self, flow_id: str, model: dict) -> dict:
33
+ return self._post(f"/v1/flow-models/{flow_id}", data=model)
34
+
35
+ def run_flow(self, flow_id: str, input: dict) -> dict:
36
+ return self._post(f"/v1/flows/{flow_id}/versions/TIP/run", data=input)
37
+
38
+ def arun_flow(self, flow_id: str, input: dict) -> dict:
39
+ return self._post(f"/v1/flows/{flow_id}/versions/TIP/run/async", data=input)
40
+