ibm-watsonx-orchestrate 1.12.2__py3-none-any.whl → 1.13.0b1__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/connections/types.py +34 -3
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +13 -2
- ibm_watsonx_orchestrate/agent_builder/models/types.py +17 -1
- ibm_watsonx_orchestrate/agent_builder/toolkits/types.py +14 -2
- ibm_watsonx_orchestrate/agent_builder/tools/__init__.py +1 -1
- ibm_watsonx_orchestrate/agent_builder/tools/types.py +21 -3
- ibm_watsonx_orchestrate/agent_builder/voice_configurations/__init__.py +1 -1
- ibm_watsonx_orchestrate/agent_builder/voice_configurations/types.py +11 -0
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +31 -53
- ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +54 -28
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_command.py +36 -2
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +270 -26
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_server_controller.py +4 -4
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +30 -3
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_environment_manager.py +158 -0
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +26 -0
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +150 -34
- ibm_watsonx_orchestrate/cli/commands/models/models_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/models/models_controller.py +29 -10
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +50 -18
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_controller.py +139 -27
- ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +2 -2
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +43 -29
- ibm_watsonx_orchestrate/cli/commands/voice_configurations/voice_configurations_controller.py +23 -11
- ibm_watsonx_orchestrate/cli/common.py +26 -0
- ibm_watsonx_orchestrate/cli/config.py +30 -1
- ibm_watsonx_orchestrate/client/agents/agent_client.py +1 -1
- ibm_watsonx_orchestrate/client/connections/connections_client.py +1 -14
- ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +55 -11
- ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +6 -2
- ibm_watsonx_orchestrate/client/model_policies/model_policies_client.py +1 -1
- ibm_watsonx_orchestrate/client/models/models_client.py +1 -1
- ibm_watsonx_orchestrate/client/threads/threads_client.py +34 -0
- ibm_watsonx_orchestrate/client/tools/tempus_client.py +4 -2
- ibm_watsonx_orchestrate/client/utils.py +29 -7
- ibm_watsonx_orchestrate/docker/compose-lite.yml +3 -2
- ibm_watsonx_orchestrate/docker/default.env +15 -10
- ibm_watsonx_orchestrate/flow_builder/flows/flow.py +28 -12
- ibm_watsonx_orchestrate/flow_builder/types.py +25 -0
- ibm_watsonx_orchestrate/flow_builder/utils.py +1 -9
- ibm_watsonx_orchestrate/utils/async_helpers.py +31 -0
- ibm_watsonx_orchestrate/utils/docker_utils.py +1177 -33
- ibm_watsonx_orchestrate/utils/environment.py +165 -20
- ibm_watsonx_orchestrate/utils/exceptions.py +1 -1
- ibm_watsonx_orchestrate/utils/tokens.py +51 -0
- ibm_watsonx_orchestrate/utils/utils.py +57 -2
- {ibm_watsonx_orchestrate-1.12.2.dist-info → ibm_watsonx_orchestrate-1.13.0b1.dist-info}/METADATA +2 -2
- {ibm_watsonx_orchestrate-1.12.2.dist-info → ibm_watsonx_orchestrate-1.13.0b1.dist-info}/RECORD +53 -48
- {ibm_watsonx_orchestrate-1.12.2.dist-info → ibm_watsonx_orchestrate-1.13.0b1.dist-info}/WHEEL +0 -0
- {ibm_watsonx_orchestrate-1.12.2.dist-info → ibm_watsonx_orchestrate-1.13.0b1.dist-info}/entry_points.txt +0 -0
- {ibm_watsonx_orchestrate-1.12.2.dist-info → ibm_watsonx_orchestrate-1.13.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -27,6 +27,9 @@ class ConnectionEnvironment(str, Enum):
|
|
27
27
|
|
28
28
|
def __str__(self):
|
29
29
|
return self.value
|
30
|
+
|
31
|
+
def __repr__(self):
|
32
|
+
return repr(self.value)
|
30
33
|
|
31
34
|
class ConnectionPreference(str, Enum):
|
32
35
|
MEMBER = 'member'
|
@@ -188,9 +191,9 @@ class ConnectionCredentialsEntryLocation(str, Enum):
|
|
188
191
|
return self.value
|
189
192
|
|
190
193
|
class ConnectionCredentialsEntry(BaseModel):
|
191
|
-
key: str
|
192
|
-
value: str
|
193
|
-
location: ConnectionCredentialsEntryLocation
|
194
|
+
key: str = Field(description="The key of the custom credential entry.")
|
195
|
+
value: str = Field(description="The value of the custom credential entry.")
|
196
|
+
location: ConnectionCredentialsEntryLocation = Field(description="How the custom credential should be sent to the server")
|
194
197
|
|
195
198
|
def __str__(self):
|
196
199
|
return f"<ConnectionCredentialsEntry: {self.location}:{self.key}={self.value}>"
|
@@ -338,3 +341,31 @@ class IdentityProviderCredentials(BaseOAuthCredentials):
|
|
338
341
|
class ExpectedCredentials(BaseModel):
|
339
342
|
app_id: str
|
340
343
|
type: ConnectionType | List[ConnectionType]
|
344
|
+
|
345
|
+
class FetchConfigAuthTypes(str, Enum):
|
346
|
+
BASIC_AUTH = ConnectionType.BASIC_AUTH.value
|
347
|
+
BEARER_TOKEN = ConnectionType.BEARER_TOKEN.value
|
348
|
+
API_KEY_AUTH = ConnectionType.API_KEY_AUTH.value
|
349
|
+
OAUTH2_AUTH_CODE = ConnectionType.OAUTH2_AUTH_CODE.value
|
350
|
+
OAUTH2_IMPLICIT = 'oauth2_implicit'
|
351
|
+
OAUTH2_PASSWORD = ConnectionType.OAUTH2_PASSWORD.value
|
352
|
+
OAUTH2_CLIENT_CREDS = ConnectionType.OAUTH2_CLIENT_CREDS.value
|
353
|
+
OAUTH_ON_BEHALF_OF_FLOW = ConnectionType.OAUTH_ON_BEHALF_OF_FLOW.value
|
354
|
+
KEY_VALUE = ConnectionType.KEY_VALUE.value
|
355
|
+
|
356
|
+
class ConnectionsListEntry(BaseModel):
|
357
|
+
app_id: str = Field(description="A unique identifier for the connection.")
|
358
|
+
auth_type: Optional[FetchConfigAuthTypes] = Field(default=None, description="The kind of auth used by the connections")
|
359
|
+
type: Optional[ConnectionPreference] = Field(default=None, description="The type of the connections. If set to 'team' the credentails will be shared by all users. If set to 'member' each user will have to provide their own credentials")
|
360
|
+
credentials_set: bool = Field(default=False, description="Are the credentials set for the current user. If using OAuth connection types this value will be False unless there isn a stored token from runtime usage")
|
361
|
+
|
362
|
+
def get_row_details(self):
|
363
|
+
auth_type = self.auth_type if self.auth_type else "n/a"
|
364
|
+
type = self.type if self.type else "n/a"
|
365
|
+
credentials_set = "✅" if self.credentials_set else "❌"
|
366
|
+
return [self.app_id, auth_type, type, credentials_set]
|
367
|
+
|
368
|
+
class ConnectionsListResponse(BaseModel):
|
369
|
+
non_configured: Optional[List[dict] | str] = None
|
370
|
+
draft: Optional[List[dict] | str] = None
|
371
|
+
live: Optional[List[dict] | str] = None
|
@@ -3,7 +3,7 @@ from datetime import datetime
|
|
3
3
|
from uuid import UUID
|
4
4
|
from enum import Enum
|
5
5
|
|
6
|
-
from pydantic import BaseModel, model_validator
|
6
|
+
from pydantic import BaseModel, model_validator, Field
|
7
7
|
|
8
8
|
|
9
9
|
class SpecVersion(str, Enum):
|
@@ -11,6 +11,7 @@ class SpecVersion(str, Enum):
|
|
11
11
|
|
12
12
|
class KnowledgeBaseKind(str, Enum):
|
13
13
|
KNOWLEDGE_BASE = "knowledge_base"
|
14
|
+
|
14
15
|
class RetrievalConfidenceThreshold(str, Enum):
|
15
16
|
Off = "Off"
|
16
17
|
Lowest = "Lowest"
|
@@ -240,6 +241,7 @@ class AstraDBConnection(BaseModel):
|
|
240
241
|
|
241
242
|
class IndexConnection(BaseModel):
|
242
243
|
connection_id: Optional[str] = None
|
244
|
+
app_id: Optional[str] = None
|
243
245
|
milvus: Optional[MilvusConnection] = None
|
244
246
|
elastic_search: Optional[ElasticSearchConnection] = None
|
245
247
|
custom_search: Optional[CustomSearchConnection] = None
|
@@ -295,4 +297,13 @@ class KnowledgeBaseSpec(BaseModel):
|
|
295
297
|
created_on: Optional[datetime] = None
|
296
298
|
updated_at: Optional[datetime] = None
|
297
299
|
# For import/update
|
298
|
-
documents: list[str] | list[FileUpload] = None
|
300
|
+
documents: list[str] | list[FileUpload] = None
|
301
|
+
|
302
|
+
class KnowledgeBaseListEntry(BaseModel):
|
303
|
+
name: str = Field(description="Name of the knowledge base")
|
304
|
+
description: Optional[str] = Field(description="A description of the content contained in a knowledge base")
|
305
|
+
app_id: Optional[str] = Field(description="The app id for a connection that connects the knowledge base to an external knowledge store")
|
306
|
+
id: Optional[str] = Field(description="Unique identifier of the knowledge base")
|
307
|
+
|
308
|
+
def get_row_details(self):
|
309
|
+
return [self.name, self.description, self.app_id, self.id]
|
@@ -226,5 +226,21 @@ class ListVirtualModel(BaseModel):
|
|
226
226
|
provider_config: Optional[ProviderConfig] = None
|
227
227
|
tags: Optional[List[str]] = None
|
228
228
|
model_type: Optional[str] = None
|
229
|
+
connection_id: Optional[str] = None
|
229
230
|
|
230
|
-
|
231
|
+
class ModelListEntry(BaseModel):
|
232
|
+
name: Optional[str] = Field(default=None, description="Name of the model")
|
233
|
+
description: Optional[str] = Field(default=None, description="A description of the model")
|
234
|
+
is_custom: bool = Field(default=False, description="Is the model a third party model imported into Orchestrate")
|
235
|
+
recommended: bool = Field(default=False, description="Is the model a reccomended model in watsonx. Non-custom models only")
|
236
|
+
|
237
|
+
def get_row_details(self):
|
238
|
+
description = self.description or 'No description provided.'
|
239
|
+
if self.is_custom:
|
240
|
+
return [f"✨️ {self.name}", description]
|
241
|
+
else:
|
242
|
+
name = self.name or "N/A"
|
243
|
+
marker = "★ " if self.recommended else ""
|
244
|
+
return [f"[yellow]{marker}[/yellow]watsonx/{name}", description]
|
245
|
+
|
246
|
+
ANTHROPIC_DEFAULT_MAX_TOKENS = 4096
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import List, Dict, Optional, Union
|
2
2
|
from enum import Enum
|
3
|
-
from pydantic import BaseModel
|
3
|
+
from pydantic import BaseModel, Field
|
4
4
|
|
5
5
|
class ToolkitKind(str, Enum):
|
6
6
|
MCP = "mcp"
|
@@ -42,4 +42,16 @@ class ToolkitSpec(BaseModel):
|
|
42
42
|
created_by: str
|
43
43
|
created_by_username: str
|
44
44
|
tools: List[str] | None
|
45
|
-
mcp: McpModel
|
45
|
+
mcp: McpModel
|
46
|
+
|
47
|
+
class ToolkitListEntry(BaseModel):
|
48
|
+
name: str = Field(description="The name of the Toolkit")
|
49
|
+
description: Optional[str] = Field(description="The description of the Toolkit")
|
50
|
+
type: str = Field(default="MCP", description="The type of Toolkit.")
|
51
|
+
tools: Optional[List[str]] = Field(description = "A list of tool names for every tool in the Toolkit")
|
52
|
+
app_ids: Optional[List[str]] = Field(description = "A list of connection app_ids showing every connection bound to the Toolkit")
|
53
|
+
|
54
|
+
def get_row_details(self):
|
55
|
+
tools = ", ".join(self.tools) if self.tools else ""
|
56
|
+
app_ids = ", ".join(self.app_ids) if self.app_ids else ""
|
57
|
+
return [self.name, self.description, self.type, tools, app_ids]
|
@@ -2,4 +2,4 @@ from .base_tool import BaseTool
|
|
2
2
|
from .python_tool import tool, PythonTool, get_all_python_tools
|
3
3
|
from .openapi_tool import create_openapi_json_tool, create_openapi_json_tool_from_uri, create_openapi_json_tools_from_uri, OpenAPITool, HTTPException
|
4
4
|
from .langflow_tool import LangflowTool
|
5
|
-
from .types import ToolPermission, JsonSchemaObject, ToolRequestBody, ToolResponseBody, OpenApiSecurityScheme, OpenApiToolBinding, PythonToolBinding, WxFlowsToolBinding, SkillToolBinding, ClientSideToolBinding, ToolBinding, ToolSpec
|
5
|
+
from .types import ToolPermission, JsonSchemaObject, ToolRequestBody, ToolResponseBody, OpenApiSecurityScheme, OpenApiToolBinding, PythonToolBinding, WxFlowsToolBinding, SkillToolBinding, ClientSideToolBinding, ToolBinding, ToolSpec, ToolListEntry
|
@@ -1,10 +1,12 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import List, Any, Dict, Literal, Optional, Union
|
3
|
+
import logging
|
3
4
|
|
4
|
-
from pydantic import BaseModel, model_validator, ConfigDict, Field, AliasChoices, ValidationError
|
5
|
+
from pydantic import BaseModel, model_validator, ConfigDict, Field, AliasChoices, ValidationError, ValidationInfo
|
5
6
|
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
6
7
|
from ibm_watsonx_orchestrate.agent_builder.connections import KeyValueConnectionCredentials
|
7
8
|
|
9
|
+
logger = logging.getLogger(__name__)
|
8
10
|
|
9
11
|
class ToolPermission(str, Enum):
|
10
12
|
READ_ONLY = 'read_only'
|
@@ -117,9 +119,14 @@ class OpenApiToolBinding(BaseModel):
|
|
117
119
|
acknowledgement: Optional[AcknowledgementBinding] = None
|
118
120
|
|
119
121
|
@model_validator(mode='after')
|
120
|
-
def validate_openapi_tool_binding(self):
|
122
|
+
def validate_openapi_tool_binding(self, info: ValidationInfo):
|
123
|
+
context = getattr(info, "context", None)
|
124
|
+
|
121
125
|
if len(self.servers) != 1:
|
122
|
-
|
126
|
+
if isinstance(context, str) and context == "list":
|
127
|
+
logger.warning("OpenAPI definition must include exactly one server")
|
128
|
+
else:
|
129
|
+
raise BadRequest("OpenAPI definition must include exactly one server")
|
123
130
|
return self
|
124
131
|
|
125
132
|
|
@@ -259,3 +266,14 @@ class ToolSpec(BaseModel):
|
|
259
266
|
return False
|
260
267
|
|
261
268
|
return True
|
269
|
+
|
270
|
+
class ToolListEntry(BaseModel):
|
271
|
+
name: str = Field(description="The name of the tool")
|
272
|
+
description: Optional[str] = Field(description="A description of the purpose of the tool")
|
273
|
+
type: Optional[str] = Field(description="The type of the tool"),
|
274
|
+
toolkit: Optional[str] = Field(description="The name of the Toolkit the tool belongs. Empty if the tool is not from a Toolkit"),
|
275
|
+
app_ids: Optional[List[str]] = Field(description="A list of app_ids that show what connections are bound to a tool")
|
276
|
+
|
277
|
+
def get_row_details(self):
|
278
|
+
app_ids = ", ".join(self.app_ids) if self.app_ids else ""
|
279
|
+
return [self.name, self.description, self.type, self.toolkit, app_ids]
|
@@ -1 +1 @@
|
|
1
|
-
from .types import VoiceConfiguration
|
1
|
+
from .types import VoiceConfiguration, VoiceConfigurationListEntry
|
@@ -95,4 +95,15 @@ class VoiceConfiguration(BaseModel):
|
|
95
95
|
dumped = self.model_dump(mode='json', exclude_none=True)
|
96
96
|
return json.dumps(dumped, indent=2)
|
97
97
|
|
98
|
+
class VoiceConfigurationListEntry(BaseModel):
|
99
|
+
name: str = Field(description="Name of the voice configuration.")
|
100
|
+
id: str = Field(default=None, description="A unique identifier for the voice configuration.")
|
101
|
+
speech_to_text_provider: Optional[str] = Field("The speech to text service provider.")
|
102
|
+
text_to_speech_provider: Optional[str] = Field("The text to speech service provider.")
|
103
|
+
attached_agents: Optional[List[str]] = Field("A list of agent names that use the voice configuration.")
|
104
|
+
|
105
|
+
def get_row_details(self):
|
106
|
+
attached_agents = ", ".join(self.attached_agents) if self.attached_agents else ""
|
107
|
+
return [self.name, self.id, self.speech_to_text_provider, self.text_to_speech_provider, attached_agents]
|
108
|
+
|
98
109
|
|
@@ -16,8 +16,9 @@ from pydantic import BaseModel
|
|
16
16
|
from ibm_watsonx_orchestrate.agent_builder.agents.types import AgentStyle
|
17
17
|
from ibm_watsonx_orchestrate.agent_builder.tools.types import ToolSpec
|
18
18
|
from ibm_watsonx_orchestrate.cli.commands.tools.tools_controller import import_python_tool, ToolsController
|
19
|
-
from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_controller import import_python_knowledge_base
|
19
|
+
from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_controller import import_python_knowledge_base, KnowledgeBaseController
|
20
20
|
from ibm_watsonx_orchestrate.cli.commands.models.models_controller import import_python_model
|
21
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats, rich_table_to_markdown
|
21
22
|
|
22
23
|
from ibm_watsonx_orchestrate.agent_builder.agents import (
|
23
24
|
Agent,
|
@@ -41,23 +42,11 @@ from ibm_watsonx_orchestrate.utils.utils import check_file_in_zip
|
|
41
42
|
from rich.console import Console
|
42
43
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
43
44
|
|
44
|
-
from enum import Enum
|
45
|
-
|
46
45
|
logger = logging.getLogger(__name__)
|
47
46
|
|
48
47
|
# Helper generic type for any agent
|
49
48
|
AnyAgentT = TypeVar("AnyAgentT", bound=Agent | ExternalAgent | AssistantAgent)
|
50
49
|
|
51
|
-
class AgentListFormats(str, Enum):
|
52
|
-
Table = "table"
|
53
|
-
JSON = "json"
|
54
|
-
|
55
|
-
def __str__(self):
|
56
|
-
return self.value
|
57
|
-
|
58
|
-
def __repr__(self):
|
59
|
-
return repr(self.value)
|
60
|
-
|
61
50
|
|
62
51
|
def import_python_agent(file: str) -> List[Agent | ExternalAgent | AssistantAgent]:
|
63
52
|
# Import tools
|
@@ -858,7 +847,7 @@ class AgentsController:
|
|
858
847
|
|
859
848
|
def _get_all_unique_agent_resources(self, agents: List[Agent], target_attr: str) -> List[str]:
|
860
849
|
"""
|
861
|
-
Given a list
|
850
|
+
Given a list of agents get all the unique values of a certain field
|
862
851
|
Example: agent1.tools = [1 ,2 ,3] and agent2.tools = [2, 4, 5] then return [1, 2, 3, 4, 5]
|
863
852
|
Example: agent1.id = "123" and agent2.id = "456" then return ["123", "456"]
|
864
853
|
|
@@ -959,18 +948,14 @@ class AgentsController:
|
|
959
948
|
if tool_names:
|
960
949
|
agent.tools = tool_names
|
961
950
|
return new_agents
|
962
|
-
|
963
|
-
# TODO: Once bulk knowledge base is added create a generaic fucntion as opposed to 3 seperate ones
|
951
|
+
|
964
952
|
def _bulk_resolve_agent_knowledge_bases(self, agents: List[Agent]) -> List[Agent]:
|
965
953
|
new_agents = agents.copy()
|
966
954
|
all_kb_ids = self._get_all_unique_agent_resources(new_agents, "knowledge_base")
|
955
|
+
if not all_kb_ids:
|
956
|
+
return new_agents
|
967
957
|
|
968
|
-
all_kbs =
|
969
|
-
for id in all_kb_ids:
|
970
|
-
try:
|
971
|
-
all_kbs.append(self.get_knowledge_base_client().get_by_id(id))
|
972
|
-
except:
|
973
|
-
continue
|
958
|
+
all_kbs = self._batch_request_resource(self.get_knowledge_base_client().get_by_ids, all_kb_ids)
|
974
959
|
|
975
960
|
kb_lut = self._construct_lut_agent_resource(all_kbs, "id", "name")
|
976
961
|
|
@@ -1016,24 +1001,7 @@ class AgentsController:
|
|
1016
1001
|
agent.app_id = app_id
|
1017
1002
|
return new_agents
|
1018
1003
|
|
1019
|
-
|
1020
|
-
def _rich_table_to_markdown(self, table: rich.table.Table) -> str:
|
1021
|
-
headers = [column.header for column in table.columns]
|
1022
|
-
cols = [[cell for cell in col.cells] for col in table.columns]
|
1023
|
-
rows = list(map(list, zip(*cols)))
|
1024
|
-
|
1025
|
-
# Header row
|
1026
|
-
md = "| " + " | ".join(headers) + " |\n"
|
1027
|
-
# Separator row
|
1028
|
-
md += "| " + " | ".join(["---"] * len(headers)) + " |\n"
|
1029
|
-
# # Data rows
|
1030
|
-
for row in rows:
|
1031
|
-
md += "| " + " | ".join(row) + " |\n"
|
1032
|
-
return md
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
def list_agents(self, kind: AgentKind=None, verbose: bool=False, format: AgentListFormats | None = None) -> dict[str, dict] | None:
|
1004
|
+
def list_agents(self, kind: AgentKind=None, verbose: bool=False, format: ListFormats | None = None) -> dict[str, dict] | dict[str, str] | None:
|
1037
1005
|
"""
|
1038
1006
|
List agents in the active wxo environment
|
1039
1007
|
|
@@ -1068,7 +1036,7 @@ class AgentsController:
|
|
1068
1036
|
resolved_native_agents = self._bulk_resolve_agent_knowledge_bases(resolved_native_agents)
|
1069
1037
|
resolved_native_agents = self._bulk_resolve_agent_collaborators(resolved_native_agents)
|
1070
1038
|
|
1071
|
-
if format and format ==
|
1039
|
+
if format and format == ListFormats.JSON:
|
1072
1040
|
agents_list = []
|
1073
1041
|
for agent in resolved_native_agents:
|
1074
1042
|
agents_list.append(json.loads(agent.dumps_spec()))
|
@@ -1096,8 +1064,9 @@ class AgentsController:
|
|
1096
1064
|
native_table.add_column(column, **column_args[column])
|
1097
1065
|
|
1098
1066
|
for agent in resolved_native_agents:
|
1067
|
+
agent_name = self._format_agent_display_name(agent)
|
1099
1068
|
native_table.add_row(
|
1100
|
-
|
1069
|
+
agent_name,
|
1101
1070
|
agent.description,
|
1102
1071
|
agent.llm,
|
1103
1072
|
agent.style,
|
@@ -1106,8 +1075,8 @@ class AgentsController:
|
|
1106
1075
|
", ".join(agent.knowledge_base),
|
1107
1076
|
agent.id,
|
1108
1077
|
)
|
1109
|
-
if format ==
|
1110
|
-
output_dictionary["native"] =
|
1078
|
+
if format == ListFormats.Table:
|
1079
|
+
output_dictionary["native"] = rich_table_to_markdown(native_table)
|
1111
1080
|
else:
|
1112
1081
|
rich.print(native_table)
|
1113
1082
|
|
@@ -1123,7 +1092,7 @@ class AgentsController:
|
|
1123
1092
|
else:
|
1124
1093
|
resolved_external_agents = self._bulk_resolve_agent_app_ids(external_agents)
|
1125
1094
|
|
1126
|
-
if format and format ==
|
1095
|
+
if format and format == ListFormats.JSON:
|
1127
1096
|
external_agents_list = []
|
1128
1097
|
for agent in resolved_external_agents:
|
1129
1098
|
external_agents_list.append(json.loads(agent.dumps_spec()))
|
@@ -1157,8 +1126,9 @@ class AgentsController:
|
|
1157
1126
|
app_id = connections_client.get_draft_by_id(agent.connection_id)
|
1158
1127
|
resolved_native_agents = self._bulk_resolve_agent_app_ids(external_agents)
|
1159
1128
|
|
1129
|
+
agent_name = self._format_agent_display_name(agent)
|
1160
1130
|
external_table.add_row(
|
1161
|
-
|
1131
|
+
agent_name,
|
1162
1132
|
agent.title,
|
1163
1133
|
agent.description,
|
1164
1134
|
", ".join(agent.tags or []),
|
@@ -1169,8 +1139,8 @@ class AgentsController:
|
|
1169
1139
|
app_id,
|
1170
1140
|
agent.id
|
1171
1141
|
)
|
1172
|
-
if format ==
|
1173
|
-
output_dictionary["external"] =
|
1142
|
+
if format == ListFormats.Table:
|
1143
|
+
output_dictionary["external"] = rich_table_to_markdown(external_table)
|
1174
1144
|
else:
|
1175
1145
|
rich.print(external_table)
|
1176
1146
|
|
@@ -1186,7 +1156,7 @@ class AgentsController:
|
|
1186
1156
|
else:
|
1187
1157
|
resolved_external_agents = self._bulk_resolve_agent_app_ids(assistant_agents)
|
1188
1158
|
|
1189
|
-
if format and format ==
|
1159
|
+
if format and format == ListFormats.JSON:
|
1190
1160
|
assistant_agents_list = []
|
1191
1161
|
for agent in resolved_external_agents:
|
1192
1162
|
assistant_agents_list.append(json.loads(agent.dumps_spec()))
|
@@ -1215,8 +1185,9 @@ class AgentsController:
|
|
1215
1185
|
assistants_table.add_column(column, **column_args[column])
|
1216
1186
|
|
1217
1187
|
for agent in assistant_agents:
|
1188
|
+
agent_name = self._format_agent_display_name(agent)
|
1218
1189
|
assistants_table.add_row(
|
1219
|
-
|
1190
|
+
agent_name,
|
1220
1191
|
agent.title,
|
1221
1192
|
agent.description,
|
1222
1193
|
", ".join(agent.tags or []),
|
@@ -1227,8 +1198,8 @@ class AgentsController:
|
|
1227
1198
|
agent.config.environment_id,
|
1228
1199
|
agent.id
|
1229
1200
|
)
|
1230
|
-
if format ==
|
1231
|
-
output_dictionary["assistant"] =
|
1201
|
+
if format == ListFormats.Table:
|
1202
|
+
output_dictionary["assistant"] = rich_table_to_markdown(assistants_table)
|
1232
1203
|
else:
|
1233
1204
|
rich.print(assistants_table)
|
1234
1205
|
|
@@ -1391,8 +1362,10 @@ class AgentsController:
|
|
1391
1362
|
ToolSpec.model_validate(current_spec).model_dump_json(exclude_unset=True,indent=2)
|
1392
1363
|
)
|
1393
1364
|
|
1365
|
+
knowledge_base_controller = KnowledgeBaseController()
|
1394
1366
|
for kb_name in agent_spec_file_content.get("knowledge_base", []):
|
1395
|
-
|
1367
|
+
knowledge_base_file_path = f"{output_file_name}/knowledge-bases/{kb_name}.yaml"
|
1368
|
+
knowledge_base_controller.knowledge_base_export(name=kb_name, output_path=knowledge_base_file_path, zip_file_out=zip_file_out)
|
1396
1369
|
|
1397
1370
|
if kind == AgentKind.NATIVE:
|
1398
1371
|
for collaborator_id in agent.collaborators:
|
@@ -1523,4 +1496,9 @@ class AgentsController:
|
|
1523
1496
|
logger.info(f"Successfully undeployed agent {name}")
|
1524
1497
|
else:
|
1525
1498
|
logger.error(f"Error undeploying agent {name}")
|
1499
|
+
|
1500
|
+
@staticmethod
|
1501
|
+
def _format_agent_display_name(agent: AnyAgentT) -> str:
|
1502
|
+
return f"{agent.name} ({agent.display_name})" if agent.display_name and agent.name != agent.display_name else agent.name
|
1503
|
+
|
1526
1504
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import typer
|
2
2
|
from typing_extensions import Annotated, List
|
3
|
-
from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionKind, ConnectionCredentialsEntry
|
3
|
+
from ibm_watsonx_orchestrate.agent_builder.connections.types import ConnectionEnvironment, ConnectionPreference, ConnectionKind, ConnectionCredentialsEntry, ConnectionSendVia
|
4
4
|
from ibm_watsonx_orchestrate.cli.commands.connections.connections_controller import (
|
5
5
|
add_connection,
|
6
6
|
remove_connection,
|
@@ -222,7 +222,7 @@ def set_credentials_connection_command(
|
|
222
222
|
)
|
223
223
|
] = None,
|
224
224
|
send_via: Annotated[
|
225
|
-
|
225
|
+
ConnectionSendVia,
|
226
226
|
typer.Option(
|
227
227
|
'--send-via',
|
228
228
|
help='For oauth_auth_client_credentials_flow, how the token will be sent to the server. Defaults to using `header`'
|
@@ -9,8 +9,9 @@ import yaml
|
|
9
9
|
import sys
|
10
10
|
import typer
|
11
11
|
|
12
|
-
from typing import List
|
12
|
+
from typing import List, Optional, Any
|
13
13
|
from ibm_watsonx_orchestrate.agent_builder.agents.types import SpecVersion
|
14
|
+
|
14
15
|
from ibm_watsonx_orchestrate.client.utils import is_local_dev
|
15
16
|
from ibm_watsonx_orchestrate.agent_builder.connections.types import (
|
16
17
|
ConnectionEnvironment,
|
@@ -34,11 +35,13 @@ from ibm_watsonx_orchestrate.agent_builder.connections.types import (
|
|
34
35
|
OAUTH_CONNECTION_TYPES,
|
35
36
|
ConnectionCredentialsEntryLocation,
|
36
37
|
ConnectionCredentialsEntry,
|
37
|
-
ConnectionCredentialsCustomFields
|
38
|
-
|
38
|
+
ConnectionCredentialsCustomFields,
|
39
|
+
ConnectionsListEntry,
|
40
|
+
ConnectionsListResponse
|
39
41
|
)
|
40
42
|
|
41
43
|
from ibm_watsonx_orchestrate.client.connections import get_connections_client, get_connection_type
|
44
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats, rich_table_to_markdown
|
42
45
|
|
43
46
|
logger = logging.getLogger(__name__)
|
44
47
|
|
@@ -470,7 +473,11 @@ def remove_connection(app_id: str) -> None:
|
|
470
473
|
logger.error(response_text)
|
471
474
|
exit(1)
|
472
475
|
|
473
|
-
def list_connections(environment: ConnectionEnvironment | None, verbose: bool = False) -> None:
|
476
|
+
def list_connections(environment: ConnectionEnvironment | None = None, verbose: bool = False, format: Optional[ListFormats] = None) -> List[dict[str, Any]]| ConnectionsListResponse | None:
|
477
|
+
if verbose and format:
|
478
|
+
logger.error("For connections list, `--verbose` and `--format` are mutually exclusive options")
|
479
|
+
sys.exit(1)
|
480
|
+
|
474
481
|
client = get_connections_client()
|
475
482
|
connections = client.list()
|
476
483
|
is_local = is_local_dev()
|
@@ -483,7 +490,12 @@ def list_connections(environment: ConnectionEnvironment | None, verbose: bool =
|
|
483
490
|
connections_list.append(json.loads(conn.model_dump_json()))
|
484
491
|
|
485
492
|
rich.print_json(json.dumps(connections_list, indent=4))
|
493
|
+
return connections_list
|
486
494
|
else:
|
495
|
+
non_configured_connection_details = []
|
496
|
+
draft_connection_details = []
|
497
|
+
live_connection_details = []
|
498
|
+
|
487
499
|
non_configured_table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True, title="*Non-Configured")
|
488
500
|
draft_table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True, title="Draft")
|
489
501
|
live_table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True, title="Live")
|
@@ -501,41 +513,55 @@ def list_connections(environment: ConnectionEnvironment | None, verbose: bool =
|
|
501
513
|
|
502
514
|
for conn in connections:
|
503
515
|
if conn.environment is None:
|
504
|
-
|
505
|
-
conn.app_id,
|
506
|
-
"n/a",
|
507
|
-
"n/a",
|
508
|
-
"❌"
|
516
|
+
entry = ConnectionsListEntry(
|
517
|
+
app_id=conn.app_id,
|
509
518
|
)
|
519
|
+
|
520
|
+
non_configured_table.add_row(*entry.get_row_details())
|
521
|
+
non_configured_connection_details.append(entry.model_dump())
|
510
522
|
continue
|
511
523
|
|
512
524
|
try:
|
513
525
|
connection_type = get_connection_type(security_scheme=conn.security_scheme, auth_type=conn.auth_type)
|
514
526
|
except:
|
515
527
|
connection_type = conn.auth_type
|
528
|
+
|
529
|
+
entry = ConnectionsListEntry(
|
530
|
+
app_id=conn.app_id,
|
531
|
+
auth_type = connection_type,
|
532
|
+
type=conn.preference,
|
533
|
+
credentials_set=conn.credentials_entered
|
534
|
+
)
|
516
535
|
|
517
536
|
if conn.environment == ConnectionEnvironment.DRAFT:
|
518
|
-
draft_table.add_row(
|
519
|
-
|
520
|
-
connection_type,
|
521
|
-
conn.preference,
|
522
|
-
"✅" if conn.credentials_entered else "❌"
|
523
|
-
)
|
537
|
+
draft_table.add_row(*entry.get_row_details())
|
538
|
+
draft_connection_details.append(entry.model_dump())
|
524
539
|
elif conn.environment == ConnectionEnvironment.LIVE and not is_local:
|
525
|
-
live_table.add_row(
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
540
|
+
live_table.add_row(*entry.get_row_details())
|
541
|
+
live_connection_details.append(entry.model_dump())
|
542
|
+
|
543
|
+
match format:
|
544
|
+
case ListFormats.JSON:
|
545
|
+
return ConnectionsListResponse(
|
546
|
+
non_configured=non_configured_connection_details,
|
547
|
+
draft=draft_connection_details,
|
548
|
+
live=live_connection_details
|
549
|
+
)
|
550
|
+
case ListFormats.Table:
|
551
|
+
return ConnectionsListResponse(
|
552
|
+
non_configured=rich_table_to_markdown(non_configured_table),
|
553
|
+
draft=rich_table_to_markdown(draft_table),
|
554
|
+
live=rich_table_to_markdown(live_table)
|
530
555
|
)
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
556
|
+
case _:
|
557
|
+
if environment is None and len(non_configured_table.rows):
|
558
|
+
rich.print(non_configured_table)
|
559
|
+
if environment == ConnectionEnvironment.DRAFT or (environment == None and len(draft_table.rows)):
|
560
|
+
rich.print(draft_table)
|
561
|
+
if environment == ConnectionEnvironment.LIVE or (environment == None and len(live_table.rows)):
|
562
|
+
rich.print(live_table)
|
563
|
+
if environment == None and not len(draft_table.rows) and not len(live_table.rows) and not len(non_configured_table.rows):
|
564
|
+
logger.info("No connections found. You can create connections using `orchestrate connections add`")
|
539
565
|
|
540
566
|
def import_connection(file: str) -> None:
|
541
567
|
_parse_file(file=file)
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import typer
|
2
2
|
from typing_extensions import Annotated
|
3
3
|
from pathlib import Path
|
4
|
-
from ibm_watsonx_orchestrate.cli.commands.copilot.copilot_controller import prompt_tune, create_agent
|
4
|
+
from ibm_watsonx_orchestrate.cli.commands.copilot.copilot_controller import prompt_tune, create_agent, \
|
5
|
+
refine_agent_with_trajectories
|
5
6
|
from ibm_watsonx_orchestrate.cli.commands.copilot.copilot_server_controller import start_server, stop_server
|
6
7
|
|
7
8
|
copilot_app = typer.Typer(no_args_is_help=True)
|
@@ -42,6 +43,10 @@ def prompt_tume_command(
|
|
42
43
|
str,
|
43
44
|
typer.Option("--llm", help="Select the agent LLM"),
|
44
45
|
] = None,
|
46
|
+
chat_llm: Annotated[
|
47
|
+
str,
|
48
|
+
typer.Option("--chat-llm", help="Select the underlying model for the copilot. Currently only llama-3-3-70b-instruct is supported."),
|
49
|
+
] = None,
|
45
50
|
samples: Annotated[
|
46
51
|
str,
|
47
52
|
typer.Option("--samples", "-s", help="Path to utterances sample file (txt file where each line is a utterance, or csv file with a single \"input\" column)"),
|
@@ -50,6 +55,7 @@ def prompt_tume_command(
|
|
50
55
|
if file is None:
|
51
56
|
# create agent yaml from scratch
|
52
57
|
create_agent(
|
58
|
+
chat_llm=chat_llm,
|
53
59
|
llm=llm,
|
54
60
|
output_file=output_file,
|
55
61
|
samples_file=samples,
|
@@ -58,8 +64,36 @@ def prompt_tume_command(
|
|
58
64
|
else:
|
59
65
|
# improve existing agent instruction
|
60
66
|
prompt_tune(
|
67
|
+
chat_llm=chat_llm,
|
61
68
|
agent_spec=file,
|
62
69
|
samples_file=samples,
|
63
70
|
output_file=output_file,
|
64
71
|
dry_run_flag=dry_run_flag,
|
65
|
-
)
|
72
|
+
)
|
73
|
+
|
74
|
+
@copilot_app.command(name="autotune", help="Autotune the agent's instructions by incorporating insights from chat interactions and user feedback")
|
75
|
+
def agent_refine(
|
76
|
+
agent_name: Annotated[
|
77
|
+
str,
|
78
|
+
typer.Option("--agent-name", "-n", help="The name of the agent to tune"),
|
79
|
+
],
|
80
|
+
output_file: Annotated[
|
81
|
+
str,
|
82
|
+
typer.Option("--output-file", "-o", help="Optional output file to avoid overwriting existing agent spec"),
|
83
|
+
] = None,
|
84
|
+
use_last_chat: Annotated[
|
85
|
+
bool,
|
86
|
+
typer.Option("--use-last-chat", "-l", help="Tuning by using the last conversation with the agent instead of prompting the user to choose chats"),
|
87
|
+
] = False,
|
88
|
+
dry_run_flag: Annotated[
|
89
|
+
bool,
|
90
|
+
typer.Option("--dry-run",
|
91
|
+
help="Dry run will prevent the tuned content being saved and output the results to console"),
|
92
|
+
] = False,
|
93
|
+
chat_llm: Annotated[
|
94
|
+
str,
|
95
|
+
typer.Option("--chat-llm", help="Select the underlying model for the copilot. Currently only llama-3-3-70b-instruct is supported."),
|
96
|
+
] = None,
|
97
|
+
|
98
|
+
):
|
99
|
+
refine_agent_with_trajectories(agent_name, chat_llm=chat_llm, output_file=output_file, use_last_chat=use_last_chat, dry_run_flag=dry_run_flag)
|