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.
- ibm_watsonx_orchestrate/__init__.py +1 -1
- ibm_watsonx_orchestrate/agent_builder/agents/types.py +6 -1
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base.py +16 -3
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +4 -20
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +13 -15
- ibm_watsonx_orchestrate/agent_builder/toolkits/base_toolkit.py +32 -0
- ibm_watsonx_orchestrate/agent_builder/toolkits/types.py +42 -0
- ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +14 -13
- ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +4 -2
- ibm_watsonx_orchestrate/agent_builder/tools/types.py +2 -1
- ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +29 -0
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +273 -12
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +79 -39
- ibm_watsonx_orchestrate/cli/commands/models/env_file_model_provider_mapper.py +180 -0
- ibm_watsonx_orchestrate/cli/commands/models/models_command.py +194 -8
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +117 -48
- ibm_watsonx_orchestrate/cli/commands/server/types.py +105 -0
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +55 -7
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +123 -42
- ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +22 -1
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +209 -25
- ibm_watsonx_orchestrate/cli/init_helper.py +43 -0
- ibm_watsonx_orchestrate/cli/main.py +3 -1
- ibm_watsonx_orchestrate/client/agents/agent_client.py +4 -1
- ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +5 -1
- ibm_watsonx_orchestrate/client/agents/external_agent_client.py +5 -1
- ibm_watsonx_orchestrate/client/analytics/llm/analytics_llm_client.py +2 -6
- ibm_watsonx_orchestrate/client/base_api_client.py +5 -2
- ibm_watsonx_orchestrate/client/connections/connections_client.py +15 -21
- ibm_watsonx_orchestrate/client/model_policies/__init__.py +0 -0
- ibm_watsonx_orchestrate/client/model_policies/model_policies_client.py +47 -0
- ibm_watsonx_orchestrate/client/model_policies/types.py +36 -0
- ibm_watsonx_orchestrate/client/models/__init__.py +0 -0
- ibm_watsonx_orchestrate/client/models/models_client.py +46 -0
- ibm_watsonx_orchestrate/client/models/types.py +177 -0
- ibm_watsonx_orchestrate/client/toolkit/toolkit_client.py +15 -6
- ibm_watsonx_orchestrate/client/tools/tempus_client.py +40 -0
- ibm_watsonx_orchestrate/client/tools/tool_client.py +8 -0
- ibm_watsonx_orchestrate/docker/compose-lite.yml +68 -13
- ibm_watsonx_orchestrate/docker/default.env +22 -12
- ibm_watsonx_orchestrate/docker/tempus/common-config.yaml +1 -1
- ibm_watsonx_orchestrate/experimental/flow_builder/__init__.py +0 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/flows/__init__.py +41 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/flows/constants.py +17 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/flows/data_map.py +91 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/flows/decorators.py +143 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/flows/events.py +72 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/flows/flow.py +1288 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/node.py +97 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/resources/flow_status.openapi.yml +98 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/types.py +492 -0
- ibm_watsonx_orchestrate/experimental/flow_builder/utils.py +113 -0
- ibm_watsonx_orchestrate/utils/utils.py +5 -2
- {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/METADATA +6 -2
- {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/RECORD +59 -36
- {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/WHEEL +0 -0
- {ibm_watsonx_orchestrate-1.2.0.dist-info → ibm_watsonx_orchestrate-1.4.2.dist-info}/entry_points.txt +0 -0
- {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) ->
|
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
|
36
|
-
|
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
|
-
|
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
|
-
|
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
|
File without changes
|
@@ -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("/
|
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(
|
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"/
|
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"/
|
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"
|
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"/
|
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
|
+
|