ibm-watsonx-orchestrate 1.3.0__py3-none-any.whl → 1.5.0b0__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 (54) hide show
  1. ibm_watsonx_orchestrate/__init__.py +2 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/types.py +2 -0
  3. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +10 -2
  4. ibm_watsonx_orchestrate/agent_builder/toolkits/base_toolkit.py +32 -0
  5. ibm_watsonx_orchestrate/agent_builder/toolkits/types.py +42 -0
  6. ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +10 -1
  7. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +4 -2
  8. ibm_watsonx_orchestrate/agent_builder/tools/types.py +2 -1
  9. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +29 -0
  10. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +271 -12
  11. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +17 -2
  12. ibm_watsonx_orchestrate/cli/commands/models/env_file_model_provider_mapper.py +180 -0
  13. ibm_watsonx_orchestrate/cli/commands/models/models_command.py +199 -8
  14. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +117 -48
  15. ibm_watsonx_orchestrate/cli/commands/server/types.py +105 -0
  16. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +55 -7
  17. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +123 -42
  18. ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +22 -1
  19. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +197 -12
  20. ibm_watsonx_orchestrate/client/agents/agent_client.py +4 -1
  21. ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +5 -1
  22. ibm_watsonx_orchestrate/client/agents/external_agent_client.py +5 -1
  23. ibm_watsonx_orchestrate/client/analytics/llm/analytics_llm_client.py +2 -6
  24. ibm_watsonx_orchestrate/client/base_api_client.py +5 -2
  25. ibm_watsonx_orchestrate/client/connections/connections_client.py +3 -9
  26. ibm_watsonx_orchestrate/client/model_policies/__init__.py +0 -0
  27. ibm_watsonx_orchestrate/client/model_policies/model_policies_client.py +47 -0
  28. ibm_watsonx_orchestrate/client/model_policies/types.py +36 -0
  29. ibm_watsonx_orchestrate/client/models/__init__.py +0 -0
  30. ibm_watsonx_orchestrate/client/models/models_client.py +46 -0
  31. ibm_watsonx_orchestrate/client/models/types.py +189 -0
  32. ibm_watsonx_orchestrate/client/toolkit/toolkit_client.py +20 -6
  33. ibm_watsonx_orchestrate/client/tools/tempus_client.py +40 -0
  34. ibm_watsonx_orchestrate/client/tools/tool_client.py +8 -0
  35. ibm_watsonx_orchestrate/docker/compose-lite.yml +68 -13
  36. ibm_watsonx_orchestrate/docker/default.env +22 -12
  37. ibm_watsonx_orchestrate/docker/tempus/common-config.yaml +1 -1
  38. ibm_watsonx_orchestrate/experimental/flow_builder/__init__.py +0 -0
  39. ibm_watsonx_orchestrate/experimental/flow_builder/data_map.py +19 -0
  40. ibm_watsonx_orchestrate/experimental/flow_builder/flows/__init__.py +42 -0
  41. ibm_watsonx_orchestrate/experimental/flow_builder/flows/constants.py +19 -0
  42. ibm_watsonx_orchestrate/experimental/flow_builder/flows/decorators.py +144 -0
  43. ibm_watsonx_orchestrate/experimental/flow_builder/flows/events.py +72 -0
  44. ibm_watsonx_orchestrate/experimental/flow_builder/flows/flow.py +1310 -0
  45. ibm_watsonx_orchestrate/experimental/flow_builder/node.py +116 -0
  46. ibm_watsonx_orchestrate/experimental/flow_builder/resources/flow_status.openapi.yml +66 -0
  47. ibm_watsonx_orchestrate/experimental/flow_builder/types.py +765 -0
  48. ibm_watsonx_orchestrate/experimental/flow_builder/utils.py +115 -0
  49. ibm_watsonx_orchestrate/utils/utils.py +5 -2
  50. {ibm_watsonx_orchestrate-1.3.0.dist-info → ibm_watsonx_orchestrate-1.5.0b0.dist-info}/METADATA +4 -1
  51. {ibm_watsonx_orchestrate-1.3.0.dist-info → ibm_watsonx_orchestrate-1.5.0b0.dist-info}/RECORD +54 -32
  52. {ibm_watsonx_orchestrate-1.3.0.dist-info → ibm_watsonx_orchestrate-1.5.0b0.dist-info}/WHEEL +0 -0
  53. {ibm_watsonx_orchestrate-1.3.0.dist-info → ibm_watsonx_orchestrate-1.5.0b0.dist-info}/entry_points.txt +0 -0
  54. {ibm_watsonx_orchestrate-1.3.0.dist-info → ibm_watsonx_orchestrate-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -147,16 +147,10 @@ class ConnectionsClient(BaseAPIClient):
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
155
  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
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,189 @@
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
+ class ModelType(str, Enum):
34
+ CHAT = 'chat'
35
+ CHAT_VISION = 'chat_vision'
36
+ COMPLETION = 'completion'
37
+ EMBEDDING = 'embedding'
38
+
39
+ def __str__(self):
40
+ return self.value
41
+
42
+ def __repr__(self):
43
+ return self.value
44
+
45
+
46
+ class ProviderConfig(BaseModel):
47
+ # Required fields
48
+ provider: Optional[str]=''
49
+
50
+
51
+ api_key: Optional[str] = '' # this is not optional
52
+ url_to_fetch: Optional[str] = Field(None, alias="urlToFetch")
53
+
54
+ # Misc
55
+ custom_host: Optional[str] = Field(None, alias="customHost")
56
+ forward_headers: Optional[List[str]] = Field(None, alias="forwardHeaders")
57
+ index: Optional[int] = None
58
+ cache: Optional[Union[str, Dict[str, Any]]] = None # Define CacheSettings if needed
59
+ metadata: Optional[Dict[str, str]] = None
60
+ request_timeout: Optional[int] = Field(None, alias="requestTimeout")
61
+ transform_to_form_data: Optional[bool] = Field(None, alias="transformToFormData")
62
+
63
+ # Azure specific
64
+ azure_resource_name: Optional[str] = Field(None, alias="resourceName")
65
+ azure_deployment_id: Optional[str] = Field(None, alias="deploymentId")
66
+ azure_api_version: Optional[str] = Field(None, alias="apiVersion")
67
+ ad_auth: Optional[str] = Field(None, alias="adAuth")
68
+ azure_auth_mode: Optional[str] = Field(None, alias="azureAuthMode")
69
+ azure_managed_client_id: Optional[str] = Field(None, alias="azureManagedClientId")
70
+ azure_entra_client_id: Optional[str] = Field(None, alias="azureEntraClientId")
71
+ azure_entra_client_secret: Optional[str] = Field(None, alias="azureEntraClientSecret")
72
+ azure_entra_tenant_id: Optional[str] = Field(None, alias="azureEntraTenantId")
73
+ azure_ad_token: Optional[str] = Field(None, alias="azureAdToken")
74
+ azure_model_name: Optional[str] = Field(None, alias="azureModelName")
75
+
76
+ # Workers AI specific
77
+ workers_ai_account_id: Optional[str] = Field(None, alias="workersAiAccountId")
78
+
79
+
80
+ # AWS
81
+ aws_secret_access_key: Optional[str] = Field(None, alias="awsSecretAccessKey")
82
+ aws_access_key_id: Optional[str] = Field(None, alias="awsAccessKeyId")
83
+ aws_session_token: Optional[str] = Field(None, alias="awsSessionToken")
84
+ aws_region: Optional[str] = Field(None, alias="awsRegion")
85
+ aws_auth_type: Optional[str] = Field(None, alias="awsAuthType")
86
+ aws_role_arn: Optional[str] = Field(None, alias="awsRoleArn")
87
+ aws_external_id: Optional[str] = Field(None, alias="awsExternalId")
88
+ aws_s3_bucket: Optional[str] = Field(None, alias="awsS3Bucket")
89
+ aws_s3_object_key: Optional[str] = Field(None, alias="awsS3ObjectKey")
90
+ aws_bedrock_model: Optional[str] = Field(None, alias="awsBedrockModel")
91
+ aws_server_side_encryption: Optional[str] = Field(None, alias="awsServerSideEncryption")
92
+ aws_server_side_encryption_kms_key_id: Optional[str] = Field(None, alias="awsServerSideEncryptionKMSKeyId")
93
+
94
+ # Sagemaker
95
+ amzn_sagemaker_custom_attributes: Optional[str] = Field(None, alias="amznSagemakerCustomAttributes")
96
+ amzn_sagemaker_target_model: Optional[str] = Field(None, alias="amznSagemakerTargetModel")
97
+ amzn_sagemaker_target_variant: Optional[str] = Field(None, alias="amznSagemakerTargetVariant")
98
+ amzn_sagemaker_target_container_hostname: Optional[str] = Field(None, alias="amznSagemakerTargetContainerHostname")
99
+ amzn_sagemaker_inference_id: Optional[str] = Field(None, alias="amznSagemakerInferenceId")
100
+ amzn_sagemaker_enable_explanations: Optional[str] = Field(None, alias="amznSagemakerEnableExplanations")
101
+ amzn_sagemaker_inference_component: Optional[str] = Field(None, alias="amznSagemakerInferenceComponent")
102
+ amzn_sagemaker_session_id: Optional[str] = Field(None, alias="amznSagemakerSessionId")
103
+ amzn_sagemaker_model_name: Optional[str] = Field(None, alias="amznSagemakerModelName")
104
+
105
+ # Stability AI
106
+ stability_client_id: Optional[str] = Field(None, alias="stabilityClientId")
107
+ stability_client_user_id: Optional[str] = Field(None, alias="stabilityClientUserId")
108
+ stability_client_version: Optional[str] = Field(None, alias="stabilityClientVersion")
109
+
110
+ # Hugging Face
111
+ huggingface_base_url: Optional[str] = Field(None, alias="huggingfaceBaseUrl")
112
+
113
+ # Google Vertex AI
114
+ vertex_region: Optional[str] = Field(None, alias="vertexRegion")
115
+ vertex_project_id: Optional[str] = Field(None, alias="vertexProjectId")
116
+ vertex_service_account_json: Optional[Dict[str, Any]] = Field(None, alias="vertexServiceAccountJson")
117
+ vertex_storage_bucket_name: Optional[str] = Field(None, alias="vertexStorageBucketName")
118
+ vertex_model_name: Optional[str] = Field(None, alias="vertexModelName")
119
+
120
+ filename: Optional[str] = None
121
+
122
+ after_request_hooks: Optional[List[Dict[str, Any]]] = Field(None, alias="afterRequestHooks")
123
+ before_request_hooks: Optional[List[Dict[str, Any]]] = Field(None, alias="beforeRequestHooks")
124
+ default_input_guardrails: Optional[List[Dict[str, Any]]] = Field(None, alias="defaultInputGuardrails")
125
+ default_output_guardrails: Optional[List[Dict[str, Any]]] = Field(None, alias="defaultOutputGuardrails")
126
+
127
+ # OpenAI
128
+ openai_project: Optional[str] = Field(None, alias="openaiProject")
129
+ openai_organization: Optional[str] = Field(None, alias="openaiOrganization")
130
+ openai_beta: Optional[str] = Field(None, alias="openaiBeta")
131
+
132
+ # Azure Inference
133
+ azure_deployment_name: Optional[str] = Field(None, alias="azureDeploymentName")
134
+ azure_api_version: Optional[str] = Field(None, alias="azureApiVersion")
135
+ azure_extra_params: Optional[str] = Field(None, alias="azureExtraParams")
136
+ azure_foundry_url: Optional[str] = Field(None, alias="azureFoundryUrl")
137
+
138
+ strict_open_ai_compliance: Optional[bool] = Field(False, alias="strictOpenAiCompliance")
139
+ mistral_fim_completion: Optional[str] = Field(None, alias="mistralFimCompletion")
140
+
141
+ # Anthropic
142
+ anthropic_beta: Optional[str] = Field(None, alias="anthropicBeta")
143
+ anthropic_version: Optional[str] = Field(None, alias="anthropicVersion")
144
+
145
+ # Fireworks
146
+ fireworks_account_id: Optional[str] = Field(None, alias="fireworksAccountId")
147
+
148
+ # Cortex
149
+ snowflake_account: Optional[str] = Field(None, alias="snowflakeAccount")
150
+
151
+ # WatsonX
152
+ watsonx_version: Optional[str] = Field(None, alias="watsonxVersion")
153
+ watsonx_space_id: Optional[str] = Field(None, alias="watsonxSpaceId")
154
+ watsonx_project_id: Optional[str] = Field(None, alias="watsonxProjectId")
155
+
156
+ model_config = {
157
+ "populate_by_name": True, # Replaces allow_population_by_field_name
158
+ "extra": "forbid", # Same as before
159
+ "json_schema_extra": lambda schema: schema.get("properties", {}).pop("provider", None)
160
+ }
161
+
162
+
163
+
164
+ class CreateVirtualModel(BaseModel):
165
+ model_config = ConfigDict(extra='allow')
166
+
167
+ name: str
168
+ display_name: Optional[str]
169
+ description: Optional[str]
170
+ config: Optional[dict] = None
171
+ provider_config: ProviderConfig
172
+ tags: List[str]
173
+ model_type: str = ModelType.CHAT
174
+
175
+
176
+ class ListVirtualModel(BaseModel):
177
+ model_config = ConfigDict(extra='allow')
178
+
179
+ id: Optional[str] = None
180
+ name: Optional[str] = None
181
+ display_name: Optional[str] = None
182
+ description: Optional[str] = None
183
+ provider: Optional[str] = None
184
+ # model_configuration: Optional[dict] = None
185
+ provider_config: Optional[ProviderConfig] = None
186
+ tags: Optional[List[str]] = None
187
+ model_type: Optional[str] = None
188
+
189
+ 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
+
12
+ def get(self) -> dict:
13
+ return self._get("/toolkits")
14
+
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,8 @@ 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("/toolkits/prepare/list-tools", files=files)
37
+
28
38
 
29
39
  return response.get("tools", [])
30
40
 
@@ -35,7 +45,8 @@ class ToolKitClient(BaseAPIClient):
35
45
  Creates new toolkit metadata
36
46
  """
37
47
  try:
38
- return self._post("/orchestrate/toolkits", data=payload)
48
+ return self._post("/toolkits", data=payload)
49
+
39
50
  except ClientAPIException as e:
40
51
  if e.response.status_code == 400 and "already exists" in e.response.text:
41
52
  raise ClientAPIException(
@@ -54,25 +65,28 @@ class ToolKitClient(BaseAPIClient):
54
65
  files = {
55
66
  "file": (filename, f, "application/zip", {"Expires": "0"})
56
67
  }
57
- return self._post(f"/orchestrate/toolkits/{toolkit_id}/upload", files=files)
68
+ return self._post(f"/toolkits/{toolkit_id}/upload", files=files)
58
69
 
59
70
  # DELETE /toolkits/{toolkit-id}
60
71
  def delete(self, toolkit_id: str) -> dict:
61
- return self._delete(f"/orchestrate/toolkits/{toolkit_id}")
72
+ return self._delete(f"/toolkits/{toolkit_id}")
73
+
62
74
 
63
75
  def get_draft_by_name(self, toolkit_name: str) -> List[dict]:
64
76
  return self.get_drafts_by_names([toolkit_name])
65
77
 
66
78
  def get_drafts_by_names(self, toolkit_names: List[str]) -> List[dict]:
67
79
  formatted_toolkit_names = [f"names={x}" for x in toolkit_names]
68
- return self._get(f"/orchestrate/toolkits?{'&'.join(formatted_toolkit_names)}")
80
+
81
+ return self._get(f"/toolkits?{'&'.join(formatted_toolkit_names)}")
82
+
69
83
 
70
84
  def get_draft_by_id(self, toolkit_id: str) -> dict:
71
85
  if toolkit_id is None:
72
86
  return ""
73
87
  else:
74
88
  try:
75
- toolkit = self._get(f"/orchestrate/toolkits/{toolkit_id}")
89
+ toolkit = self._get(f"/toolkits/{toolkit_id}")
76
90
  return toolkit
77
91
  except ClientAPIException as e:
78
92
  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
+
@@ -21,6 +21,10 @@ class ToolClient(BaseAPIClient):
21
21
  def upload_tools_artifact(self, tool_id: str, file_path: str) -> dict:
22
22
  return self._post(f"/tools/{tool_id}/upload", files={"file": (f"{tool_id}.zip", open(file_path, "rb"), "application/zip", {"Expires": "0"})})
23
23
 
24
+ def download_tools_artifact(self, tool_id: str) -> bytes:
25
+ response = self._get(f"/tools/{tool_id}/download", return_raw=True)
26
+ return response.content
27
+
24
28
  def get_draft_by_name(self, tool_name: str) -> List[dict]:
25
29
  return self.get_drafts_by_names([tool_name])
26
30
 
@@ -39,3 +43,7 @@ class ToolClient(BaseAPIClient):
39
43
  if e.response.status_code == 404 and "not found with the given name" in e.response.text:
40
44
  return ""
41
45
  raise(e)
46
+
47
+ def get_drafts_by_ids(self, tool_ids: List[str]) -> List[dict]:
48
+ formatted_tool_ids = [f"ids={x}" for x in tool_ids]
49
+ return self._get(f"/tools?{'&'.join(formatted_tool_ids)}")
@@ -49,6 +49,17 @@ services:
49
49
  ports:
50
50
  - 3001:3001
51
51
 
52
+ ai-gateway:
53
+ image: ${AI_GATEWAY_REGISTRY:-us.icr.io/watson-orchestrate-private}/ai-gateway:${AI_GATEWAY_TAG:-latest}
54
+ platform: linux/amd64
55
+ restart: unless-stopped
56
+ ports:
57
+ - "8787:8787"
58
+ environment:
59
+ WATSONX_API_KEY: ${WATSONX_APIKEY}
60
+ WATSONX_URL: ${WATSONX_URL}
61
+ WATSONX_SPACE_ID: ${WATSONX_SPACE_ID}
62
+
52
63
  ui:
53
64
  image: ${UI_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-chat:${UITAG:-latest}
54
65
  platform: linux/amd64
@@ -95,6 +106,7 @@ services:
95
106
  environment:
96
107
  WXO_DEPLOYMENT_TYPE: 'laptop'
97
108
  AGENT_RUNTIME_ENDPOINT: http://wxo-server:4321
109
+ TEMPUS_RUNTIME_ENDPOINT: http://wxo-tempus-runtime:9044
98
110
  command: 'npm start'
99
111
  ports:
100
112
  - '4025:4025'
@@ -210,14 +222,21 @@ services:
210
222
  volumes:
211
223
  - ./sdk:/packages
212
224
  - tools:/tools
225
+
213
226
  command: >
214
- bash -c "mkdir -p /tmp/certs &&
215
- openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/certs/key.pem -out /tmp/certs/cert.pem -subj '/CN=localhost' &&
216
- uvicorn --host 0.0.0.0 --port 4322 --ssl-keyfile /tmp/certs/key.pem --ssl-certfile /tmp/certs/cert.pem --log-config /app/config/logs/log_conf.yaml --workers 2 --log-level debug wo_archer.api.main:app &
217
- (for i in {1..20}; do curl --silent --fail -k https://127.0.0.1:4322/health/alive && echo '[INFO] HTTPS Service ready' && break || echo '[INFO] Waiting for HTTPS service...' && sleep 30; done; curl --silent --fail -k https://127.0.0.1:4322/health/alive || (echo '[ERROR] HTTPS service failed to start' && exit 1) ) &&
218
- uvicorn --host 0.0.0.0 --port 4321 --log-config /app/config/logs/log_conf.yaml --log-level debug --workers 5 wo_archer.api.main:app &
219
- (for i in {1..20}; do curl --silent --fail -k http://127.0.0.1:4321/health/alive && echo '[INFO] HTTP Service ready' && break || echo '[INFO] Waiting for HTTP service...' && sleep 30; done; curl --silent --fail -k http://127.0.0.1:4321/health/alive || (echo '[ERROR] HTTP service failed to start' && exit 1) ) &&
220
- wait"
227
+ bash -c '
228
+ export PYTHONPATH="/tmp/monkey:$PYTHONPATH"
229
+ mkdir -p /tmp/monkey &&
230
+
231
+ echo -e "from uvicorn.supervisors.multiprocess import Process\n_original_is_alive = Process.is_alive\ndef patched_is_alive(self, timeout: float = 30):\n return _original_is_alive(self, timeout=30)\nProcess.is_alive = patched_is_alive" > /tmp/monkey/sitecustomize.py &&
232
+
233
+ uvicorn --host 0.0.0.0 --port 4321 --log-config /app/config/logs/log_conf.yaml --log-level debug --workers 5 wo_archer.api.main:app &
234
+
235
+ for i in {1..40}; do curl --silent --fail http://127.0.0.1:4321/health/alive && echo "[INFO] HTTP Service ready" && break || echo "[INFO] Waiting for HTTP service..." && sleep 10; done;
236
+ curl --silent --fail http://127.0.0.1:4321/health/alive || (echo "[ERROR] HTTP service failed to start" && exit 1);
237
+
238
+ wait
239
+ '
221
240
  depends_on:
222
241
  wxo-server-redis:
223
242
  condition: service_started
@@ -294,6 +313,15 @@ services:
294
313
  MILVUS_DB_NAME: default
295
314
  MILVUS_USERNAME: root
296
315
  MILVUS_PASSWORD: Milvus
316
+ WO_API_KEY: ${WO_API_KEY}
317
+ WO_USERNAME: ${WO_USERNAME}
318
+ WO_PASSWORD: ${WO_PASSWORD}
319
+ WO_INSTANCE: ${WO_INSTANCE}
320
+ USE_SAAS_ML_TOOLS_RUNTIME: ${USE_SAAS_ML_TOOLS_RUNTIME}
321
+ AUTHORIZATION_URL: ${AUTHORIZATION_URL}
322
+ WO_AUTH_TYPE: ${WO_AUTH_TYPE}
323
+ AI_GATEWAY_BASE_URL: ${AI_GATEWAY_BASE_URL}
324
+ AI_GATEWAY_ENABLED : ${AI_GATEWAY_ENABLED}
297
325
 
298
326
  wxo-server-worker:
299
327
  image: ${WORKER_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-server-conversation_controller:${WORKER_TAG:-latest}
@@ -311,6 +339,8 @@ services:
311
339
  - ./sdk:/packages
312
340
  - tools:/tools
313
341
  environment:
342
+ AI_GATEWAY_ENABLED : ${AI_GATEWAY_ENABLED}
343
+ AI_GATEWAY_BASE_URL: ${AI_GATEWAY_BASE_URL}
314
344
  IS_WXO_LITE: 'TRUE'
315
345
  TRM_BASE_URL: http://tools-runtime-manager:8080
316
346
  AGENT_STEP_DETAILS: redis://wxo-server-redis:6379/0
@@ -372,6 +402,13 @@ services:
372
402
  MILVUS_DB_NAME: default
373
403
  MILVUS_USERNAME: root
374
404
  MILVUS_PASSWORD: Milvus
405
+ WO_API_KEY: ${WO_API_KEY}
406
+ WO_USERNAME: ${WO_USERNAME}
407
+ WO_PASSWORD: ${WO_PASSWORD}
408
+ WO_INSTANCE: ${WO_INSTANCE}
409
+ USE_SAAS_ML_TOOLS_RUNTIME: ${USE_SAAS_ML_TOOLS_RUNTIME}
410
+ AUTHORIZATION_URL: ${AUTHORIZATION_URL}
411
+ WO_AUTH_TYPE: ${WO_AUTH_TYPE}
375
412
 
376
413
  tools-runtime-manager:
377
414
  image: ${TRM_REGISTRY:-us.icr.io/watson-orchestrate-private}/tools-runtime-manager:${TRM_TAG:-latest}
@@ -407,6 +444,12 @@ services:
407
444
  VOLUME_MOUNT_PATH: "/shared-data"
408
445
  TOOLS_RUNTIME_MANAGER_BASE_URL: ${TOOLS_RUNTIME_MANAGER_BASE_URL}
409
446
  CONNECTION_SERVICE_BASE_URL: http://wxo-server-connection-manager:3001
447
+ STORAGE_S3_BUCKET: ${STORAGE_S3_BUCKET:-wxo-server-storage-bucket}
448
+ STORAGE_S3_ENDPOINT: http://wxo-server-minio:9000
449
+ STORAGE_S3_FORCE_PATH_STYLE: 'true'
450
+ STORAGE_S3_REGION: us-east-1
451
+ AWS_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-minioadmin}
452
+ AWS_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-watsonxorchestrate}
410
453
  extra_hosts:
411
454
  - "host.docker.internal:host-gateway"
412
455
 
@@ -432,6 +475,12 @@ services:
432
475
  DB_PASSWORD: ${DB_PASSWORD:-postgres}
433
476
  DB_NAME: ${DB_NAME:-postgres}
434
477
  DB_SSLMODE: ${DB_SSLMODE:-disable} # Disable SSL if not configured
478
+ STORAGE_S3_BUCKET: ${STORAGE_S3_BUCKET:-wxo-server-storage-bucket}
479
+ STORAGE_S3_ENDPOINT: http://wxo-server-minio:9000
480
+ STORAGE_S3_FORCE_PATH_STYLE: 'true'
481
+ STORAGE_S3_REGION: us-east-1
482
+ AWS_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-minioadmin}
483
+ AWS_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-watsonxorchestrate}
435
484
 
436
485
  ########################
437
486
  # LANGFUSE dependencies
@@ -531,11 +580,8 @@ services:
531
580
  wxo-tempus-runtime:
532
581
  image: ${FLOW_RUMTIME_REGISTRY:-us.icr.io/watson-orchestrate-private}/wxo-tempus-runtime:${FLOW_RUNTIME_TAG:-latest}
533
582
  restart: unless-stopped
534
- platform: linux/amd64
535
- profiles: [with-tempus-runtime]
536
583
  environment:
537
584
  NODE_TLS_REJECT_UNAUTHORIZED: "0"
538
- COMMON_CONFIG_FILE: "/shared/common-config.yaml"
539
585
  SERVER_MODE: production
540
586
  SERVER_ENVIRONMENT: SDK
541
587
  SERVER_HTTP_PORT: "9044"
@@ -557,6 +603,17 @@ services:
557
603
  PG_DATABASE: ${POSTGRES_USER:-postgres}
558
604
  USER_MGMT_BASE_URL: "" # TODO
559
605
  WXO_SERVER_BASE_URL: http://wxo-server:4321
606
+ TRM_SERVER_BASE_URL: http://tools-runtime-manager:8080
607
+ CONNECTION_MANAGER_SERVER_BASE_URL: http://wxo-server-connection-manager:3001
608
+ REDIS_ENDPOINT: wxo-server-redis:6379
609
+ REDIS_USER:
610
+ REDIS_PASSWORD:
611
+ REDIS_TLS: false
612
+ LANGFUSE_ENABLED: ${LANGFUSE_ENABLED:-false}
613
+ LANGFUSE_HOST: ${LANGFUSE_HOST:-http://langfuse-web:3000}
614
+ LANGFUSE_PUBLIC_KEY: ${LANGFUSE_PUBLIC_KEY:-pk-lf-7417757e-d6df-421b-957e-683b76acb5df}
615
+ LANGFUSE_SECRET_KEY: ${LANGFUSE_PRIVATE_KEY:-sk-lf-7bc4da63-7b2b-40c0-b5eb-1e0cf64f9af2}
616
+ LOG_LEVEL: info
560
617
  healthcheck:
561
618
  test: curl -k http://localhost:9044/readiness --fail
562
619
  interval: 5s
@@ -564,8 +621,6 @@ services:
564
621
  retries: 5
565
622
  ports:
566
623
  - 9044:9044
567
- volumes:
568
- - ./tempus/common-config.yaml:/shared/common-config.yaml
569
624
  depends_on:
570
625
  - wxo-server-db
571
626
 
@@ -595,4 +650,4 @@ volumes:
595
650
  networks:
596
651
  default:
597
652
  name: wxo-server
598
-
653
+