ibm-watsonx-orchestrate 1.0.1__tar.gz → 1.2.0__tar.gz
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-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/PKG-INFO +1 -1
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/__init__.py +2 -1
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +7 -2
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/tools/types.py +18 -5
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/server/server_command.py +179 -52
- ibm_watsonx_orchestrate-1.2.0/src/ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +71 -0
- ibm_watsonx_orchestrate-1.2.0/src/ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +212 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +40 -17
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/config.py +5 -1
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/main.py +7 -5
- ibm_watsonx_orchestrate-1.2.0/src/ibm_watsonx_orchestrate/client/toolkit/toolkit_client.py +81 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/docker/compose-lite.yml +13 -10
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/docker/default.env +21 -18
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/.gitignore +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/LICENSE +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/pyproject.toml +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/agents/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/agents/agent.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/agents/types.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/connections/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/connections/connections.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/connections/types.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/tools/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/utils/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/channels/channels_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/channels/channels_controller.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/channels/types.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/chat/chat_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/environment/environment_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/environment/types.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/login/login_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/models/models_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/settings/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/settings/observability/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/langfuse_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/settings/observability/observability_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/settings/settings_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/cli/commands/tools/types.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/agents/agent_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/agents/external_agent_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/analytics/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/analytics/llm/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/analytics/llm/analytics_llm_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/base_api_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/base_service_instance.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/client_errors.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/connections/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/connections/connections_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/connections/utils.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/credentials.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/local_service_instance.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/service_instance.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/tools/tool_client.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/client/utils.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0-py3-none-any.whl +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0.tar.gz +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/docker/start-up.sh +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/docker/tempus/common-config.yaml +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/run/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/run/connections.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/utils/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/utils/logging/__init__.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/utils/logging/logger.py +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/utils/logging/logging.yaml +0 -0
- {ibm_watsonx_orchestrate-1.0.1 → ibm_watsonx_orchestrate-1.2.0}/src/ibm_watsonx_orchestrate/utils/utils.py +0 -0
@@ -121,6 +121,7 @@ def tool(
|
|
121
121
|
_desc = description
|
122
122
|
if description is None and doc is not None:
|
123
123
|
_desc = doc.description
|
124
|
+
|
124
125
|
|
125
126
|
spec = ToolSpec(
|
126
127
|
name=name or fn.__name__,
|
@@ -150,7 +151,11 @@ def tool(
|
|
150
151
|
|
151
152
|
sig = inspect.signature(fn)
|
152
153
|
if not input_schema:
|
153
|
-
|
154
|
+
try:
|
155
|
+
input_schema_model: type[BaseModel] = create_schema_from_function(spec.name, fn, parse_docstring=True)
|
156
|
+
except:
|
157
|
+
logger.warning("Unable to properly parse parameter descriptions due to incorrectly formatted docstring. This may result in degraded agent performance. To fix this, please ensure the docstring conforms to Google's docstring format.")
|
158
|
+
input_schema_model: type[BaseModel] = create_schema_from_function(spec.name, fn, parse_docstring=False)
|
154
159
|
input_schema_json = input_schema_model.model_json_schema()
|
155
160
|
input_schema_json = dereference_refs(input_schema_json)
|
156
161
|
|
@@ -165,7 +170,7 @@ def tool(
|
|
165
170
|
)
|
166
171
|
else:
|
167
172
|
spec.input_schema = input_schema
|
168
|
-
|
173
|
+
|
169
174
|
_validate_input_schema(spec.input_schema)
|
170
175
|
|
171
176
|
if not output_schema:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from enum import Enum
|
2
|
-
from typing import List, Any, Dict, Literal, Optional
|
2
|
+
from typing import List, Any, Dict, Literal, Optional, Union
|
3
3
|
|
4
4
|
from pydantic import BaseModel, model_validator, ConfigDict, Field, AliasChoices
|
5
5
|
|
@@ -14,7 +14,7 @@ class ToolPermission(str, Enum):
|
|
14
14
|
class JsonSchemaObject(BaseModel):
|
15
15
|
model_config = ConfigDict(extra='allow')
|
16
16
|
|
17
|
-
type: Optional[Literal['object', 'string', 'number', 'integer', 'boolean', 'array', 'null']] = None
|
17
|
+
type: Optional[Union[Literal['object', 'string', 'number', 'integer', 'boolean', 'array', 'null'], List[Literal['object', 'string', 'number', 'integer', 'boolean', 'array', 'null']]]] = None
|
18
18
|
title: str | None = None
|
19
19
|
description: str | None = None
|
20
20
|
properties: Optional[Dict[str, 'JsonSchemaObject']] = None
|
@@ -34,13 +34,19 @@ class JsonSchemaObject(BaseModel):
|
|
34
34
|
aliasName: str | None = None
|
35
35
|
"Runtime feature where the sdk can provide the original name of a field before prefixing"
|
36
36
|
|
37
|
+
@model_validator(mode='after')
|
38
|
+
def normalize_type_field(self) -> 'JsonSchemaObject':
|
39
|
+
if isinstance(self.type, list):
|
40
|
+
self.type = self.type[0]
|
41
|
+
return self
|
42
|
+
|
37
43
|
|
38
44
|
class ToolRequestBody(BaseModel):
|
39
45
|
model_config = ConfigDict(extra='allow')
|
40
46
|
|
41
47
|
type: Literal['object']
|
42
48
|
properties: Dict[str, JsonSchemaObject]
|
43
|
-
required: List[str]
|
49
|
+
required: Optional[List[str]] = None
|
44
50
|
|
45
51
|
|
46
52
|
class ToolResponseBody(BaseModel):
|
@@ -52,7 +58,7 @@ class ToolResponseBody(BaseModel):
|
|
52
58
|
items: JsonSchemaObject = None
|
53
59
|
uniqueItems: bool = None
|
54
60
|
anyOf: List['JsonSchemaObject'] = None
|
55
|
-
required: List[str] = None
|
61
|
+
required: Optional[List[str]] = None
|
56
62
|
|
57
63
|
class OpenApiSecurityScheme(BaseModel):
|
58
64
|
type: Literal['apiKey', 'http', 'oauth2', 'openIdConnect']
|
@@ -128,6 +134,10 @@ class SkillToolBinding(BaseModel):
|
|
128
134
|
class ClientSideToolBinding(BaseModel):
|
129
135
|
pass
|
130
136
|
|
137
|
+
class McpToolBinding(BaseModel):
|
138
|
+
server_url: Optional[str] = None
|
139
|
+
source: str
|
140
|
+
connections: Dict[str, str]
|
131
141
|
|
132
142
|
class ToolBinding(BaseModel):
|
133
143
|
openapi: OpenApiToolBinding = None
|
@@ -135,6 +145,7 @@ class ToolBinding(BaseModel):
|
|
135
145
|
wxflows: WxFlowsToolBinding = None
|
136
146
|
skill: SkillToolBinding = None
|
137
147
|
client_side: ClientSideToolBinding = None
|
148
|
+
mcp: McpToolBinding = None
|
138
149
|
|
139
150
|
@model_validator(mode='after')
|
140
151
|
def validate_binding_type(self) -> 'ToolBinding':
|
@@ -143,7 +154,8 @@ class ToolBinding(BaseModel):
|
|
143
154
|
self.python is not None,
|
144
155
|
self.wxflows is not None,
|
145
156
|
self.skill is not None,
|
146
|
-
self.client_side is not None
|
157
|
+
self.client_side is not None,
|
158
|
+
self.mcp is not None
|
147
159
|
]
|
148
160
|
if sum(bindings) == 0:
|
149
161
|
raise ValueError("One binding must be set")
|
@@ -159,4 +171,5 @@ class ToolSpec(BaseModel):
|
|
159
171
|
input_schema: ToolRequestBody = None
|
160
172
|
output_schema: ToolResponseBody = None
|
161
173
|
binding: ToolBinding = None
|
174
|
+
toolkit_id: str | None = None
|
162
175
|
|
@@ -1,30 +1,25 @@
|
|
1
|
+
import importlib.resources as resources
|
1
2
|
import logging
|
2
|
-
import
|
3
|
+
import os
|
4
|
+
import platform
|
3
5
|
import subprocess
|
6
|
+
import sys
|
4
7
|
import tempfile
|
5
|
-
from pathlib import Path
|
6
|
-
import requests
|
7
8
|
import time
|
8
|
-
import
|
9
|
-
import platform
|
10
|
-
|
9
|
+
from pathlib import Path
|
11
10
|
|
12
|
-
import typer
|
13
|
-
import importlib.resources as resources
|
14
11
|
import jwt
|
12
|
+
import requests
|
13
|
+
import typer
|
14
|
+
from dotenv import dotenv_values
|
15
15
|
|
16
|
-
from dotenv import dotenv_values, load_dotenv
|
17
|
-
|
18
|
-
from ibm_watsonx_orchestrate.client.agents.agent_client import AgentClient
|
19
|
-
from ibm_watsonx_orchestrate.client.analytics.llm.analytics_llm_client import AnalyticsLLMClient, AnalyticsLLMConfig, \
|
20
|
-
AnalyticsLLMUpsertToolIdentifier
|
21
16
|
from ibm_watsonx_orchestrate.client.utils import instantiate_client, check_token_validity, is_local_dev
|
22
|
-
|
23
|
-
from ibm_watsonx_orchestrate.cli.
|
17
|
+
from ibm_watsonx_orchestrate.cli.commands.environment.environment_controller import _login
|
18
|
+
from ibm_watsonx_orchestrate.cli.config import LICENSE_HEADER, \
|
19
|
+
ENV_ACCEPT_LICENSE
|
24
20
|
from ibm_watsonx_orchestrate.cli.config import PROTECTED_ENV_NAME, clear_protected_env_credentials_token, Config, \
|
25
|
-
AUTH_CONFIG_FILE_FOLDER, AUTH_CONFIG_FILE, AUTH_MCSP_TOKEN_OPT,
|
26
|
-
|
27
|
-
from dotenv import dotenv_values, load_dotenv
|
21
|
+
AUTH_CONFIG_FILE_FOLDER, AUTH_CONFIG_FILE, AUTH_MCSP_TOKEN_OPT, AUTH_SECTION_HEADER, USER_ENV_CACHE_HEADER
|
22
|
+
from ibm_watsonx_orchestrate.client.agents.agent_client import AgentClient
|
28
23
|
|
29
24
|
logger = logging.getLogger(__name__)
|
30
25
|
|
@@ -52,11 +47,11 @@ def ensure_docker_compose_installed() -> list:
|
|
52
47
|
typer.echo("Unable to find an installed docker-compose or docker compose")
|
53
48
|
sys.exit(1)
|
54
49
|
|
55
|
-
def docker_login(
|
50
|
+
def docker_login(api_key: str, registry_url: str, username:str = "iamapikey") -> None:
|
56
51
|
logger.info(f"Logging into Docker registry: {registry_url} ...")
|
57
52
|
result = subprocess.run(
|
58
|
-
["docker", "login", "-u",
|
59
|
-
input=
|
53
|
+
["docker", "login", "-u", username, "--password-stdin", registry_url],
|
54
|
+
input=api_key.encode("utf-8"),
|
60
55
|
capture_output=True,
|
61
56
|
)
|
62
57
|
if result.returncode != 0:
|
@@ -64,6 +59,24 @@ def docker_login(iam_api_key: str, registry_url: str) -> None:
|
|
64
59
|
sys.exit(1)
|
65
60
|
logger.info("Successfully logged in to Docker.")
|
66
61
|
|
62
|
+
def docker_login_by_dev_edition_source(env_dict: dict, source: str) -> None:
|
63
|
+
registry_url = env_dict["REGISTRY_URL"]
|
64
|
+
if source == "internal":
|
65
|
+
iam_api_key = env_dict.get("DOCKER_IAM_KEY")
|
66
|
+
if not iam_api_key:
|
67
|
+
raise ValueError("DOCKER_IAM_KEY is required in the environment file if WO_DEVELOPER_EDITION_SOURCE is set to 'internal'.")
|
68
|
+
docker_login(iam_api_key, registry_url, "iamapikey")
|
69
|
+
elif source == "myibm":
|
70
|
+
wo_entitlement_key = env_dict.get("WO_ENTITLEMENT_KEY")
|
71
|
+
if not wo_entitlement_key:
|
72
|
+
raise ValueError("WO_ENTITLEMENT_KEY is required in the environment file.")
|
73
|
+
docker_login(wo_entitlement_key, registry_url, "cp")
|
74
|
+
elif source == "orchestrate":
|
75
|
+
wo_auth_type = env_dict.get("WO_AUTH_TYPE")
|
76
|
+
if not wo_auth_type:
|
77
|
+
raise ValueError("WO_AUTH_TYPE is required in the environment file if WO_DEVELOPER_EDITION_SOURCE is set to 'orchestrate'.")
|
78
|
+
api_key, username = get_docker_cred_by_wo_auth_type(env_dict, wo_auth_type)
|
79
|
+
docker_login(api_key, registry_url, username)
|
67
80
|
|
68
81
|
def get_compose_file() -> Path:
|
69
82
|
with resources.as_file(
|
@@ -93,9 +106,51 @@ def merge_env(
|
|
93
106
|
user_env = dotenv_values(str(user_env_path))
|
94
107
|
merged.update(user_env)
|
95
108
|
|
96
|
-
|
97
109
|
return merged
|
98
110
|
|
111
|
+
def get_default_registry_env_vars_by_dev_edition_source(env_dict: dict, source: str) -> dict[str,str]:
|
112
|
+
component_registry_var_names = {key for key in env_dict if key.endswith("_REGISTRY")}
|
113
|
+
|
114
|
+
result = {}
|
115
|
+
if source == "internal":
|
116
|
+
result["REGISTRY_URL"] = "us.icr.io"
|
117
|
+
for name in component_registry_var_names:
|
118
|
+
result[name] = "us.icr.io/watson-orchestrate-private"
|
119
|
+
elif source == "myibm":
|
120
|
+
result["REGISTRY_URL"] = "cp.icr.io"
|
121
|
+
for name in component_registry_var_names:
|
122
|
+
result[name] = "cp.icr.io/cp/wxo-lite"
|
123
|
+
elif source == "orchestrate":
|
124
|
+
raise NotImplementedError("The 'orchestrate' source is not implemented yet.")
|
125
|
+
# TODO: confirm with Tej about the registry url for orchestrate source
|
126
|
+
return result
|
127
|
+
|
128
|
+
def get_dev_edition_source(env_dict: dict) -> str:
|
129
|
+
source = env_dict.get("WO_DEVELOPER_EDITION_SOURCE")
|
130
|
+
|
131
|
+
if source:
|
132
|
+
return source
|
133
|
+
if env_dict.get("WO_INSTANCE"):
|
134
|
+
return "orchestrate"
|
135
|
+
return "myibm"
|
136
|
+
|
137
|
+
def get_docker_cred_by_wo_auth_type(env_dict: dict, auth_type: str) -> tuple[str, str]:
|
138
|
+
if auth_type in {"mcsp", "ibm_iam"}:
|
139
|
+
wo_api_key = env_dict.get("WO_API_KEY")
|
140
|
+
if not wo_api_key:
|
141
|
+
raise ValueError("WO_API_KEY is required in the environment file if the WO_AUTH_TYPE is set to 'mcsp' or 'ibm_iam'.")
|
142
|
+
return wo_api_key, "wouser"
|
143
|
+
elif auth_type == "cpd":
|
144
|
+
wo_api_key = env_dict.get("WO_API_KEY")
|
145
|
+
wo_password = env_dict.get("WO_PASSWORD")
|
146
|
+
if not wo_api_key and not wo_password:
|
147
|
+
raise ValueError("WO_API_KEY or WO_PASSWORD is required in the environment file if the WO_AUTH_TYPE is set to 'cpd'.")
|
148
|
+
wo_username = env_dict.get("WO_USERNAME")
|
149
|
+
if not wo_username:
|
150
|
+
raise ValueError("WO_USERNAME is required in the environment file if the WO_AUTH_TYPE is set to 'cpd'.")
|
151
|
+
return wo_api_key or wo_password, wo_username # type: ignore[return-value]
|
152
|
+
else:
|
153
|
+
raise ValueError(f"Unknown value for WO_AUTH_TYPE: {auth_type}. Must be one of ['mcsp', 'ibm_iam', 'cpd'].")
|
99
154
|
|
100
155
|
def apply_llm_api_key_defaults(env_dict: dict) -> None:
|
101
156
|
llm_value = env_dict.get("WATSONX_APIKEY")
|
@@ -138,7 +193,30 @@ def refresh_local_credentials() -> None:
|
|
138
193
|
clear_protected_env_credentials_token()
|
139
194
|
_login(name=PROTECTED_ENV_NAME, apikey=None)
|
140
195
|
|
196
|
+
NON_SECRET_ENV_ITEMS = {
|
197
|
+
"WO_DEVELOPER_EDITION_SOURCE",
|
198
|
+
"WO_INSTANCE",
|
199
|
+
"USE_SAAS_ML_TOOLS_RUNTIME",
|
200
|
+
"WXO_MCSP_EXCHANGE_URL",
|
201
|
+
"OPENSOURCE_REGISTRY_PROXY"
|
202
|
+
}
|
203
|
+
def persist_user_env(env: dict, include_secrets: bool = False) -> None:
|
204
|
+
if include_secrets:
|
205
|
+
persistable_env = env
|
206
|
+
else:
|
207
|
+
persistable_env = {k:env[k] for k in NON_SECRET_ENV_ITEMS if k in env}
|
141
208
|
|
209
|
+
cfg = Config()
|
210
|
+
cfg.save(
|
211
|
+
{
|
212
|
+
USER_ENV_CACHE_HEADER: persistable_env
|
213
|
+
}
|
214
|
+
)
|
215
|
+
|
216
|
+
def get_persisted_user_env() -> dict | None:
|
217
|
+
cfg = Config()
|
218
|
+
user_env = cfg.get(USER_ENV_CACHE_HEADER) if cfg.get(USER_ENV_CACHE_HEADER) else None
|
219
|
+
return user_env
|
142
220
|
|
143
221
|
def run_compose_lite(final_env_file: Path, experimental_with_langfuse=False, with_flow_runtime=False) -> None:
|
144
222
|
compose_path = get_compose_file()
|
@@ -256,12 +334,20 @@ def run_compose_lite_ui(user_env_file: Path) -> bool:
|
|
256
334
|
compose_path = get_compose_file()
|
257
335
|
compose_command = ensure_docker_compose_installed()
|
258
336
|
ensure_docker_installed()
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
337
|
+
|
338
|
+
default_env = read_env_file(get_default_env_file())
|
339
|
+
user_env = read_env_file(user_env_file) if user_env_file else {}
|
340
|
+
if not user_env:
|
341
|
+
user_env = get_persisted_user_env()
|
342
|
+
|
343
|
+
dev_edition_source = get_dev_edition_source(user_env)
|
344
|
+
default_registry_vars = get_default_registry_env_vars_by_dev_edition_source(default_env, source=dev_edition_source)
|
345
|
+
|
346
|
+
merged_env_dict = {
|
347
|
+
**default_env,
|
348
|
+
**default_registry_vars,
|
349
|
+
**user_env,
|
350
|
+
}
|
265
351
|
|
266
352
|
_login(name=PROTECTED_ENV_NAME)
|
267
353
|
auth_cfg = Config(AUTH_CONFIG_FILE_FOLDER, AUTH_CONFIG_FILE)
|
@@ -271,21 +357,17 @@ def run_compose_lite_ui(user_env_file: Path) -> bool:
|
|
271
357
|
tenant_id = token.get('woTenantId', None)
|
272
358
|
merged_env_dict['REACT_APP_TENANT_ID'] = tenant_id
|
273
359
|
|
274
|
-
|
275
|
-
registry_url = merged_env_dict.get("REGISTRY_URL")
|
276
|
-
if not registry_url:
|
277
|
-
logger.error("Error: REGISTRY_URL is required in the environment file.")
|
278
|
-
sys.exit(1)
|
279
|
-
|
280
360
|
agent_client = instantiate_client(AgentClient)
|
281
361
|
agents = agent_client.get()
|
282
362
|
if not agents:
|
283
363
|
logger.error("No agents found for the current environment. Please create an agent before starting the chat.")
|
284
364
|
sys.exit(1)
|
285
365
|
|
286
|
-
|
287
|
-
|
288
|
-
|
366
|
+
try:
|
367
|
+
docker_login_by_dev_edition_source(merged_env_dict, dev_edition_source)
|
368
|
+
except ValueError as ignored:
|
369
|
+
# do nothing, as the docker login here is not mandatory
|
370
|
+
pass
|
289
371
|
|
290
372
|
#These are to removed warning and not used in UI component
|
291
373
|
if not 'WATSONX_SPACE_ID' in merged_env_dict:
|
@@ -436,6 +518,35 @@ def run_compose_lite_logs(final_env_file: Path, is_reset: bool = False) -> None:
|
|
436
518
|
)
|
437
519
|
sys.exit(1)
|
438
520
|
|
521
|
+
def confirm_accepts_license_agreement(accepts_by_argument: bool):
|
522
|
+
cfg = Config()
|
523
|
+
accepts_license = cfg.read(LICENSE_HEADER, ENV_ACCEPT_LICENSE)
|
524
|
+
if accepts_license != True:
|
525
|
+
logger.warning(('''
|
526
|
+
By running the following command your machine will install IBM watsonx Orchestrate Developer Edition, which is governed by the following IBM license agreement:
|
527
|
+
- * https://www.ibm.com/support/customer/csol/terms/?id=L-YRMZ-PB6MHM&lc=en
|
528
|
+
Additionally, the following prerequisite open source programs will be obtained from Docker Hub and will be installed on your machine. Each of the below programs are Separately Licensed Code, and are governed by the separate license agreements identified below, and not by the IBM license agreement:
|
529
|
+
* redis (7.2) - https://github.com/redis/redis/blob/7.2.7/COPYING
|
530
|
+
* minio - https://github.com/minio/minio/blob/master/LICENSE
|
531
|
+
* milvus-io - https://github.com/milvus-io/milvus/blob/master/LICENSE
|
532
|
+
* etcd - https://github.com/etcd-io/etcd/blob/main/LICENSE
|
533
|
+
* clickhouse-server - https://github.com/ClickHouse/ClickHouse/blob/master/LICENSE
|
534
|
+
* langfuse - https://github.com/langfuse/langfuse/blob/main/LICENSE
|
535
|
+
After installation, you are solely responsible for obtaining and installing updates and fixes, including security patches, for the above prerequisite open source programs. To update images the customer will run `orchestrate server reset && orchestrate server start -e .env`.
|
536
|
+
''').strip())
|
537
|
+
if not accepts_by_argument:
|
538
|
+
result = input('\nTo accept the terms and conditions of the IBM license agreement and the Separately Licensed Code licenses above please enter "I accept": ')
|
539
|
+
else:
|
540
|
+
result = None
|
541
|
+
if result == 'I accept' or accepts_by_argument:
|
542
|
+
cfg.write(LICENSE_HEADER, ENV_ACCEPT_LICENSE, True)
|
543
|
+
else:
|
544
|
+
logger.error('The terms and conditions were not accepted, exiting.')
|
545
|
+
exit(1)
|
546
|
+
|
547
|
+
|
548
|
+
|
549
|
+
|
439
550
|
@server_app.command(name="start")
|
440
551
|
def server_start(
|
441
552
|
user_env_file: str = typer.Option(
|
@@ -454,33 +565,46 @@ def server_start(
|
|
454
565
|
help='Option to start server with tempus-runtime.',
|
455
566
|
hidden=True
|
456
567
|
)
|
568
|
+
,
|
569
|
+
persist_env_secrets: bool = typer.Option(
|
570
|
+
False,
|
571
|
+
'--persist-env-secrets', '-p',
|
572
|
+
help='Option to store secret values from the provided env file in the config file (~/.config/orchestrate/config.yaml)',
|
573
|
+
hidden=True
|
574
|
+
),
|
575
|
+
accept_terms_and_conditions: bool = typer.Option(
|
576
|
+
False,
|
577
|
+
"--accept-terms-and-conditions",
|
578
|
+
help="By providing this flag you accept the terms and conditions outlined in the logs on server start."
|
579
|
+
),
|
457
580
|
):
|
581
|
+
confirm_accepts_license_agreement(accept_terms_and_conditions)
|
582
|
+
|
458
583
|
if user_env_file and not Path(user_env_file).exists():
|
459
584
|
logger.error(f"Error: The specified environment file '{user_env_file}' does not exist.")
|
460
585
|
sys.exit(1)
|
461
586
|
ensure_docker_installed()
|
462
587
|
|
463
|
-
|
588
|
+
default_env = read_env_file(get_default_env_file())
|
589
|
+
user_env = read_env_file(user_env_file) if user_env_file else {}
|
590
|
+
persist_user_env(user_env, include_secrets=persist_env_secrets)
|
591
|
+
dev_edition_source = get_dev_edition_source(user_env)
|
592
|
+
default_registry_vars = get_default_registry_env_vars_by_dev_edition_source(default_env, source=dev_edition_source)
|
464
593
|
|
465
|
-
merged_env_dict =
|
466
|
-
|
467
|
-
|
468
|
-
|
594
|
+
merged_env_dict = {
|
595
|
+
**default_env,
|
596
|
+
**default_registry_vars,
|
597
|
+
**user_env,
|
598
|
+
}
|
469
599
|
|
470
600
|
merged_env_dict['DBTAG'] = get_dbtag_from_architecture(merged_env_dict=merged_env_dict)
|
471
601
|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
registry_url = merged_env_dict.get("REGISTRY_URL")
|
478
|
-
if not registry_url:
|
479
|
-
logger.error("Error: REGISTRY_URL is required in the environment file.")
|
602
|
+
try:
|
603
|
+
docker_login_by_dev_edition_source(merged_env_dict, dev_edition_source)
|
604
|
+
except ValueError as e:
|
605
|
+
logger.error(f"Error: {e}")
|
480
606
|
sys.exit(1)
|
481
607
|
|
482
|
-
docker_login(iam_api_key, registry_url)
|
483
|
-
|
484
608
|
apply_llm_api_key_defaults(merged_env_dict)
|
485
609
|
|
486
610
|
|
@@ -511,6 +635,9 @@ def server_start(
|
|
511
635
|
|
512
636
|
logger.info(f"You can run `orchestrate env activate local` to set your environment or `orchestrate chat start` to start the UI service and begin chatting.")
|
513
637
|
|
638
|
+
if experimental_with_langfuse:
|
639
|
+
logger.info(f"You can access the observability platform Langfuse at http://localhost:3010, username: orchestrate@ibm.com, password: orchestrate")
|
640
|
+
|
514
641
|
if with_flow_runtime:
|
515
642
|
logger.info(f"Starting with flow runtime")
|
516
643
|
|
ibm_watsonx_orchestrate-1.2.0/src/ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
import typer
|
2
|
+
from typing import List
|
3
|
+
from typing_extensions import Annotated, Optional
|
4
|
+
from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_controller import ToolkitController, ToolkitKind
|
5
|
+
|
6
|
+
toolkits_app = typer.Typer(no_args_is_help=True)
|
7
|
+
|
8
|
+
@toolkits_app.command(name="import")
|
9
|
+
def import_toolkit(
|
10
|
+
kind: Annotated[
|
11
|
+
ToolkitKind,
|
12
|
+
typer.Option("--kind", "-k", help="Kind of toolkit, currently only MCP is supported"),
|
13
|
+
],
|
14
|
+
name: Annotated[
|
15
|
+
str,
|
16
|
+
typer.Option("--name", "-n", help="Name of the toolkit"),
|
17
|
+
],
|
18
|
+
description: Annotated[
|
19
|
+
str,
|
20
|
+
typer.Option("--description", help="Description of the toolkit"),
|
21
|
+
],
|
22
|
+
package_root: Annotated[
|
23
|
+
str,
|
24
|
+
typer.Option("--package-root", "-p", help="Root directory of the MCP server package"),
|
25
|
+
],
|
26
|
+
command: Annotated[
|
27
|
+
str,
|
28
|
+
typer.Option(
|
29
|
+
"--command",
|
30
|
+
help="Command to start the MCP server. Can be a string (e.g. 'node dist/index.js --transport stdio') "
|
31
|
+
"or a JSON-style list of arguments (e.g. '[\"node\", \"dist/index.js\", \"--transport\", \"stdio\"]'). "
|
32
|
+
"The first argument will be used as the executable, the rest as its arguments."
|
33
|
+
),
|
34
|
+
],
|
35
|
+
tools: Annotated[
|
36
|
+
Optional[str],
|
37
|
+
typer.Option("--tools", "-t", help="Comma-separated list of tools to import. Or you can use `*` to use all tools"),
|
38
|
+
] = None,
|
39
|
+
app_id: Annotated[
|
40
|
+
List[str],
|
41
|
+
typer.Option(
|
42
|
+
"--app-id", "-a",
|
43
|
+
help='The app id of the connection to associate with this tool. A application connection represents the server authentication credentials needed to connect to this tool. Only type key_value is currently supported for MCP.'
|
44
|
+
)
|
45
|
+
] = None
|
46
|
+
):
|
47
|
+
if tools == "*":
|
48
|
+
tool_list = ["*"] # Wildcard to use all tools
|
49
|
+
elif tools:
|
50
|
+
tool_list = [tool.strip() for tool in tools.split(",")]
|
51
|
+
else:
|
52
|
+
tool_list = None
|
53
|
+
|
54
|
+
toolkit_controller = ToolkitController(
|
55
|
+
kind=kind,
|
56
|
+
name=name,
|
57
|
+
description=description,
|
58
|
+
package_root=package_root,
|
59
|
+
command=command,
|
60
|
+
)
|
61
|
+
toolkit_controller.import_toolkit(tools=tool_list, app_id=app_id)
|
62
|
+
|
63
|
+
@toolkits_app.command(name="remove")
|
64
|
+
def remove_toolkit(
|
65
|
+
name: Annotated[
|
66
|
+
str,
|
67
|
+
typer.Option("--name", "-n", help="Name of the toolkit you wish to remove"),
|
68
|
+
],
|
69
|
+
):
|
70
|
+
toolkit_controller = ToolkitController()
|
71
|
+
toolkit_controller.remove_toolkit(name=name)
|