ibm-watsonx-orchestrate 1.0.0__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 +28 -0
- ibm_watsonx_orchestrate/agent_builder/__init__.py +0 -0
- ibm_watsonx_orchestrate/agent_builder/agents/__init__.py +5 -0
- ibm_watsonx_orchestrate/agent_builder/agents/agent.py +27 -0
- ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +28 -0
- ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +28 -0
- ibm_watsonx_orchestrate/agent_builder/agents/types.py +204 -0
- ibm_watsonx_orchestrate/agent_builder/connections/__init__.py +27 -0
- ibm_watsonx_orchestrate/agent_builder/connections/connections.py +123 -0
- ibm_watsonx_orchestrate/agent_builder/connections/types.py +260 -0
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base.py +27 -0
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +59 -0
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +243 -0
- ibm_watsonx_orchestrate/agent_builder/tools/__init__.py +4 -0
- ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +36 -0
- ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +332 -0
- ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +195 -0
- ibm_watsonx_orchestrate/agent_builder/tools/types.py +162 -0
- ibm_watsonx_orchestrate/agent_builder/utils/__init__.py +0 -0
- ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py +149 -0
- ibm_watsonx_orchestrate/cli/__init__.py +0 -0
- ibm_watsonx_orchestrate/cli/commands/__init__.py +0 -0
- ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +192 -0
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +660 -0
- ibm_watsonx_orchestrate/cli/commands/channels/channels_command.py +15 -0
- ibm_watsonx_orchestrate/cli/commands/channels/channels_controller.py +16 -0
- ibm_watsonx_orchestrate/cli/commands/channels/types.py +15 -0
- ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_command.py +32 -0
- ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +141 -0
- ibm_watsonx_orchestrate/cli/commands/chat/chat_command.py +43 -0
- ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +307 -0
- ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +517 -0
- ibm_watsonx_orchestrate/cli/commands/environment/environment_command.py +78 -0
- ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +189 -0
- ibm_watsonx_orchestrate/cli/commands/environment/types.py +9 -0
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +79 -0
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +201 -0
- ibm_watsonx_orchestrate/cli/commands/login/login_command.py +17 -0
- ibm_watsonx_orchestrate/cli/commands/models/models_command.py +128 -0
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +623 -0
- ibm_watsonx_orchestrate/cli/commands/settings/__init__.py +0 -0
- ibm_watsonx_orchestrate/cli/commands/settings/observability/__init__.py +0 -0
- ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/__init__.py +0 -0
- ibm_watsonx_orchestrate/cli/commands/settings/observability/langfuse/langfuse_command.py +175 -0
- ibm_watsonx_orchestrate/cli/commands/settings/observability/observability_command.py +11 -0
- ibm_watsonx_orchestrate/cli/commands/settings/settings_command.py +10 -0
- ibm_watsonx_orchestrate/cli/commands/tools/tools_command.py +85 -0
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +564 -0
- ibm_watsonx_orchestrate/cli/commands/tools/types.py +10 -0
- ibm_watsonx_orchestrate/cli/config.py +226 -0
- ibm_watsonx_orchestrate/cli/main.py +32 -0
- ibm_watsonx_orchestrate/client/__init__.py +0 -0
- ibm_watsonx_orchestrate/client/agents/agent_client.py +46 -0
- ibm_watsonx_orchestrate/client/agents/assistant_agent_client.py +38 -0
- ibm_watsonx_orchestrate/client/agents/external_agent_client.py +38 -0
- ibm_watsonx_orchestrate/client/analytics/__init__.py +0 -0
- ibm_watsonx_orchestrate/client/analytics/llm/__init__.py +0 -0
- ibm_watsonx_orchestrate/client/analytics/llm/analytics_llm_client.py +50 -0
- ibm_watsonx_orchestrate/client/base_api_client.py +113 -0
- ibm_watsonx_orchestrate/client/base_service_instance.py +10 -0
- ibm_watsonx_orchestrate/client/client.py +71 -0
- ibm_watsonx_orchestrate/client/client_errors.py +359 -0
- ibm_watsonx_orchestrate/client/connections/__init__.py +10 -0
- ibm_watsonx_orchestrate/client/connections/connections_client.py +162 -0
- ibm_watsonx_orchestrate/client/connections/utils.py +27 -0
- ibm_watsonx_orchestrate/client/credentials.py +123 -0
- ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +46 -0
- ibm_watsonx_orchestrate/client/local_service_instance.py +91 -0
- ibm_watsonx_orchestrate/client/service_instance.py +73 -0
- ibm_watsonx_orchestrate/client/tools/tool_client.py +41 -0
- ibm_watsonx_orchestrate/client/utils.py +95 -0
- ibm_watsonx_orchestrate/docker/compose-lite.yml +595 -0
- ibm_watsonx_orchestrate/docker/default.env +125 -0
- ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0-py3-none-any.whl +0 -0
- ibm_watsonx_orchestrate/docker/sdk/ibm_watsonx_orchestrate-0.6.0.tar.gz +0 -0
- ibm_watsonx_orchestrate/docker/start-up.sh +61 -0
- ibm_watsonx_orchestrate/docker/tempus/common-config.yaml +1 -0
- ibm_watsonx_orchestrate/run/__init__.py +0 -0
- ibm_watsonx_orchestrate/run/connections.py +40 -0
- ibm_watsonx_orchestrate/utils/__init__.py +0 -0
- ibm_watsonx_orchestrate/utils/logging/__init__.py +0 -0
- ibm_watsonx_orchestrate/utils/logging/logger.py +26 -0
- ibm_watsonx_orchestrate/utils/logging/logging.yaml +18 -0
- ibm_watsonx_orchestrate/utils/utils.py +15 -0
- ibm_watsonx_orchestrate-1.0.0.dist-info/METADATA +34 -0
- ibm_watsonx_orchestrate-1.0.0.dist-info/RECORD +89 -0
- ibm_watsonx_orchestrate-1.0.0.dist-info/WHEEL +4 -0
- ibm_watsonx_orchestrate-1.0.0.dist-info/entry_points.txt +2 -0
- ibm_watsonx_orchestrate-1.0.0.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,660 @@
|
|
1
|
+
import yaml
|
2
|
+
import json
|
3
|
+
import rich
|
4
|
+
import requests
|
5
|
+
import importlib
|
6
|
+
import inspect
|
7
|
+
import sys
|
8
|
+
import logging
|
9
|
+
from pathlib import Path
|
10
|
+
from copy import deepcopy
|
11
|
+
|
12
|
+
from typing import Iterable, List
|
13
|
+
from ibm_watsonx_orchestrate.cli.commands.tools.tools_controller import import_python_tool
|
14
|
+
|
15
|
+
from ibm_watsonx_orchestrate.agent_builder.agents import (
|
16
|
+
Agent,
|
17
|
+
ExternalAgent,
|
18
|
+
AssistantAgent,
|
19
|
+
AgentKind,
|
20
|
+
SpecVersion
|
21
|
+
)
|
22
|
+
from ibm_watsonx_orchestrate.client.agents.agent_client import AgentClient
|
23
|
+
from ibm_watsonx_orchestrate.client.agents.external_agent_client import ExternalAgentClient
|
24
|
+
from ibm_watsonx_orchestrate.client.agents.assistant_agent_client import AssistantAgentClient
|
25
|
+
from ibm_watsonx_orchestrate.client.tools.tool_client import ToolClient
|
26
|
+
from ibm_watsonx_orchestrate.client.connections import get_connections_client
|
27
|
+
from ibm_watsonx_orchestrate.client.knowledge_bases.knowledge_base_client import KnowledgeBaseClient
|
28
|
+
|
29
|
+
from ibm_watsonx_orchestrate.client.utils import instantiate_client
|
30
|
+
|
31
|
+
logger = logging.getLogger(__name__)
|
32
|
+
|
33
|
+
def import_python_agent(file: str) -> List[Agent | ExternalAgent | AssistantAgent]:
|
34
|
+
# Import tools
|
35
|
+
import_python_tool(file)
|
36
|
+
|
37
|
+
file_path = Path(file)
|
38
|
+
file_directory = file_path.parent
|
39
|
+
file_name = file_path.stem
|
40
|
+
sys.path.append(str(file_directory))
|
41
|
+
module = importlib.import_module(file_name)
|
42
|
+
del sys.path[-1]
|
43
|
+
|
44
|
+
agents = []
|
45
|
+
for _, obj in inspect.getmembers(module):
|
46
|
+
if isinstance(obj, Agent) or isinstance(obj, ExternalAgent) or isinstance(obj, AssistantAgent):
|
47
|
+
agents.append(obj)
|
48
|
+
return agents
|
49
|
+
|
50
|
+
|
51
|
+
def create_agent_from_spec(file:str, kind:str) -> Agent | ExternalAgent | AssistantAgent:
|
52
|
+
if not kind:
|
53
|
+
kind = AgentKind.NATIVE
|
54
|
+
match kind:
|
55
|
+
case AgentKind.NATIVE:
|
56
|
+
agent = Agent.from_spec(file)
|
57
|
+
case AgentKind.EXTERNAL:
|
58
|
+
agent = ExternalAgent.from_spec(file)
|
59
|
+
case AgentKind.ASSISTANT:
|
60
|
+
agent = AssistantAgent.from_spec(file)
|
61
|
+
case _:
|
62
|
+
raise ValueError("'kind' must be either 'native' or 'external'")
|
63
|
+
|
64
|
+
return agent
|
65
|
+
|
66
|
+
def parse_file(file: str) -> List[Agent | ExternalAgent | AssistantAgent]:
|
67
|
+
if file.endswith('.yaml') or file.endswith('.yml') or file.endswith(".json"):
|
68
|
+
with open(file, 'r') as f:
|
69
|
+
if file.endswith(".json"):
|
70
|
+
content = json.load(f)
|
71
|
+
else:
|
72
|
+
content = yaml.load(f, Loader=yaml.SafeLoader)
|
73
|
+
agent = create_agent_from_spec(file=file, kind=content.get("kind"))
|
74
|
+
return [agent]
|
75
|
+
elif file.endswith('.py'):
|
76
|
+
agents = import_python_agent(file)
|
77
|
+
return agents
|
78
|
+
else:
|
79
|
+
raise ValueError("file must end in .json, .yaml, .yml or .py")
|
80
|
+
|
81
|
+
def parse_create_native_args(name: str, kind: AgentKind, description: str | None, **args) -> dict:
|
82
|
+
agent_details = {
|
83
|
+
"name": name,
|
84
|
+
"kind": kind,
|
85
|
+
"description": description,
|
86
|
+
"llm": args.get("llm"),
|
87
|
+
"style": args.get("style"),
|
88
|
+
}
|
89
|
+
|
90
|
+
collaborators = args.get("collaborators", [])
|
91
|
+
collaborators = collaborators if collaborators else []
|
92
|
+
collaborators = [x.strip() for x in collaborators if x.strip() != ""]
|
93
|
+
agent_details["collaborators"] = collaborators
|
94
|
+
|
95
|
+
tools = args.get("tools", [])
|
96
|
+
tools = tools if tools else []
|
97
|
+
tools = [x.strip() for x in tools if x.strip() != ""]
|
98
|
+
agent_details["tools"] = tools
|
99
|
+
|
100
|
+
knowledge_base = args.get("knowledge_base", [])
|
101
|
+
knowledge_base = knowledge_base if knowledge_base else []
|
102
|
+
knowledge_base = [x.strip() for x in knowledge_base if x.strip() != ""]
|
103
|
+
agent_details["knowledge_base"] = knowledge_base
|
104
|
+
|
105
|
+
return agent_details
|
106
|
+
|
107
|
+
def parse_create_external_args(name: str, kind: AgentKind, description: str | None, **args) -> dict:
|
108
|
+
agent_details = {
|
109
|
+
"name": name,
|
110
|
+
"kind": kind,
|
111
|
+
"description": description,
|
112
|
+
"title": args.get("title"),
|
113
|
+
"api_url": args.get("api_url"),
|
114
|
+
"auth_scheme": args.get("auth_scheme"),
|
115
|
+
"auth_config": args.get("auth_config", {}),
|
116
|
+
"provider": args.get("provider"),
|
117
|
+
"tags": args.get("tags", []),
|
118
|
+
"chat_params": args.get("chat_params", {}),
|
119
|
+
"config": args.get("config", {}),
|
120
|
+
"nickname": args.get("nickname"),
|
121
|
+
"app_id": args.get("app_id"),
|
122
|
+
}
|
123
|
+
|
124
|
+
return agent_details
|
125
|
+
|
126
|
+
def parse_create_assistant_args(name: str, kind: AgentKind, description: str | None, **args) -> dict:
|
127
|
+
agent_details = {
|
128
|
+
"name": name,
|
129
|
+
"kind": kind,
|
130
|
+
"description": description,
|
131
|
+
"title": args.get("title"),
|
132
|
+
"tags": args.get("tags", []),
|
133
|
+
"config": args.get("config", {}),
|
134
|
+
"nickname": args.get("nickname"),
|
135
|
+
}
|
136
|
+
|
137
|
+
return agent_details
|
138
|
+
|
139
|
+
def get_conn_id_from_app_id(app_id: str) -> str:
|
140
|
+
connections_client = get_connections_client()
|
141
|
+
connection = connections_client.get_draft_by_app_id(app_id=app_id)
|
142
|
+
if not connection:
|
143
|
+
logger.error(f"No connection exits with the app-id '{app_id}'")
|
144
|
+
exit(1)
|
145
|
+
return connection.connection_id
|
146
|
+
|
147
|
+
class AgentsController:
|
148
|
+
def __init__(self):
|
149
|
+
self.native_client = None
|
150
|
+
self.external_client = None
|
151
|
+
self.assistant_client = None
|
152
|
+
self.tool_client = None
|
153
|
+
self.knowledge_base_client = None
|
154
|
+
|
155
|
+
def get_native_client(self):
|
156
|
+
if not self.native_client:
|
157
|
+
self.native_client = instantiate_client(AgentClient)
|
158
|
+
return self.native_client
|
159
|
+
|
160
|
+
def get_external_client(self):
|
161
|
+
if not self.external_client:
|
162
|
+
self.external_client = instantiate_client(ExternalAgentClient)
|
163
|
+
return self.external_client
|
164
|
+
|
165
|
+
def get_assistant_client(self):
|
166
|
+
if not self.assistant_client:
|
167
|
+
self.assistant_client = instantiate_client(AssistantAgentClient)
|
168
|
+
return self.assistant_client
|
169
|
+
|
170
|
+
def get_tool_client(self):
|
171
|
+
if not self.tool_client:
|
172
|
+
self.tool_client = instantiate_client(ToolClient)
|
173
|
+
return self.tool_client
|
174
|
+
|
175
|
+
def get_knowledge_base_client(self):
|
176
|
+
if not self.knowledge_base_client:
|
177
|
+
self.knowledge_base_client = instantiate_client(KnowledgeBaseClient)
|
178
|
+
return self.knowledge_base_client
|
179
|
+
|
180
|
+
@staticmethod
|
181
|
+
def import_agent(file: str, app_id: str) -> Iterable:
|
182
|
+
agents = parse_file(file)
|
183
|
+
for agent in agents:
|
184
|
+
if app_id and agent.kind != AgentKind.NATIVE and agent.kind != AgentKind.ASSISTANT:
|
185
|
+
agent.app_id = app_id
|
186
|
+
return agents
|
187
|
+
|
188
|
+
|
189
|
+
@staticmethod
|
190
|
+
def generate_agent_spec(
|
191
|
+
name: str, kind: AgentKind, description: str, **kwargs
|
192
|
+
) -> Agent | ExternalAgent | AssistantAgent:
|
193
|
+
match kind:
|
194
|
+
case AgentKind.NATIVE:
|
195
|
+
agent_details = parse_create_native_args(name, kind=kind, description=description, **kwargs)
|
196
|
+
agent = Agent.model_validate(agent_details)
|
197
|
+
AgentsController().persist_record(agent=agent, **kwargs)
|
198
|
+
case AgentKind.EXTERNAL:
|
199
|
+
agent_details = parse_create_external_args(name, kind=kind, description=description, **kwargs)
|
200
|
+
agent = ExternalAgent.model_validate(agent_details)
|
201
|
+
AgentsController().persist_record(agent=agent, **kwargs)
|
202
|
+
# for agents command without --app-id
|
203
|
+
if kwargs.get("app_id") is not None:
|
204
|
+
connection_id = get_conn_id_from_app_id(kwargs.get("app_id"))
|
205
|
+
|
206
|
+
agent.connection_id = connection_id
|
207
|
+
case AgentKind.ASSISTANT:
|
208
|
+
agent_details = parse_create_assistant_args(name, kind=kind, description=description, **kwargs)
|
209
|
+
agent = AssistantAgent.model_validate(agent_details)
|
210
|
+
AgentsController().persist_record(agent=agent, **kwargs)
|
211
|
+
case _:
|
212
|
+
raise ValueError("'kind' must be 'native' or 'external' for agent creation")
|
213
|
+
return agent
|
214
|
+
|
215
|
+
def get_all_agents(self, client: None):
|
216
|
+
return {entry["name"]: entry["id"] for entry in client.get()}
|
217
|
+
|
218
|
+
def dereference_collaborators(self, agent: Agent) -> Agent:
|
219
|
+
native_client = self.get_native_client()
|
220
|
+
external_client = self.get_external_client()
|
221
|
+
assistant_client = self.get_assistant_client()
|
222
|
+
|
223
|
+
deref_agent = deepcopy(agent)
|
224
|
+
matching_native_agents = native_client.get_drafts_by_names(deref_agent.collaborators)
|
225
|
+
matching_external_agents = external_client.get_drafts_by_names(deref_agent.collaborators)
|
226
|
+
matching_assistant_agents = assistant_client.get_drafts_by_names(deref_agent.collaborators)
|
227
|
+
matching_agents = matching_native_agents + matching_external_agents + matching_assistant_agents
|
228
|
+
name_id_lookup = {}
|
229
|
+
for a in matching_agents:
|
230
|
+
if a.get("name") in name_id_lookup:
|
231
|
+
logger.error(f"Duplicate draft entries for collaborator '{a.get('name')}'")
|
232
|
+
sys.exit(1)
|
233
|
+
name_id_lookup[a.get("name")] = a.get("id")
|
234
|
+
|
235
|
+
deref_collaborators = []
|
236
|
+
for name in agent.collaborators:
|
237
|
+
id = name_id_lookup.get(name)
|
238
|
+
if not id:
|
239
|
+
logger.error(f"Failed to find collaborator. No agents found with the name '{name}'")
|
240
|
+
sys.exit(1)
|
241
|
+
deref_collaborators.append(id)
|
242
|
+
deref_agent.collaborators = deref_collaborators
|
243
|
+
|
244
|
+
return deref_agent
|
245
|
+
|
246
|
+
def dereference_tools(self, agent: Agent) -> Agent:
|
247
|
+
tool_client = self.get_tool_client()
|
248
|
+
|
249
|
+
deref_agent = deepcopy(agent)
|
250
|
+
matching_tools = tool_client.get_drafts_by_names(deref_agent.tools)
|
251
|
+
|
252
|
+
name_id_lookup = {}
|
253
|
+
for tool in matching_tools:
|
254
|
+
if tool.get("name") in name_id_lookup:
|
255
|
+
logger.error(f"Duplicate draft entries for tol '{tool.get('name')}'")
|
256
|
+
sys.exit(1)
|
257
|
+
name_id_lookup[tool.get("name")] = tool.get("id")
|
258
|
+
|
259
|
+
deref_tools = []
|
260
|
+
for name in agent.tools:
|
261
|
+
id = name_id_lookup.get(name)
|
262
|
+
if not id:
|
263
|
+
logger.error(f"Failed to find tool. No tools found with the name '{name}'")
|
264
|
+
sys.exit(1)
|
265
|
+
deref_tools.append(id)
|
266
|
+
deref_agent.tools = deref_tools
|
267
|
+
|
268
|
+
return deref_agent
|
269
|
+
|
270
|
+
def dereference_knowledge_bases(self, agent: Agent) -> Agent:
|
271
|
+
client = self.get_knowledge_base_client()
|
272
|
+
|
273
|
+
deref_agent = deepcopy(agent)
|
274
|
+
matching_knowledge_bases = client.get_by_names(deref_agent.knowledge_base)
|
275
|
+
|
276
|
+
name_id_lookup = {}
|
277
|
+
for kb in matching_knowledge_bases:
|
278
|
+
if kb.get("name") in name_id_lookup:
|
279
|
+
logger.error(f"Duplicate draft entries for knowledge base '{kb.get('name')}'")
|
280
|
+
sys.exit(1)
|
281
|
+
name_id_lookup[kb.get("name")] = kb.get("id")
|
282
|
+
|
283
|
+
deref_knowledge_bases = []
|
284
|
+
for name in agent.knowledge_base:
|
285
|
+
id = name_id_lookup.get(name)
|
286
|
+
if not id:
|
287
|
+
logger.error(f"Failed to find knowledge base. No knowledge base found with the name '{name}'")
|
288
|
+
sys.exit(1)
|
289
|
+
deref_knowledge_bases.append(id)
|
290
|
+
deref_agent.knowledge_base = deref_knowledge_bases
|
291
|
+
|
292
|
+
return deref_agent
|
293
|
+
|
294
|
+
@staticmethod
|
295
|
+
def dereference_app_id(agent: ExternalAgent | AssistantAgent) -> ExternalAgent | AssistantAgent:
|
296
|
+
if agent.kind == AgentKind.EXTERNAL:
|
297
|
+
agent.connection_id = get_conn_id_from_app_id(agent.app_id)
|
298
|
+
else:
|
299
|
+
agent.config.connection_id = get_conn_id_from_app_id(agent.config.app_id)
|
300
|
+
|
301
|
+
return agent
|
302
|
+
|
303
|
+
|
304
|
+
def dereference_native_agent_dependencies(self, agent: Agent) -> Agent:
|
305
|
+
if agent.collaborators and len(agent.collaborators):
|
306
|
+
agent = self.dereference_collaborators(agent)
|
307
|
+
if agent.tools and len(agent.tools):
|
308
|
+
agent = self.dereference_tools(agent)
|
309
|
+
if agent.knowledge_base and len(agent.knowledge_base):
|
310
|
+
agent = self.dereference_knowledge_bases(agent)
|
311
|
+
|
312
|
+
return agent
|
313
|
+
|
314
|
+
def dereference_external_or_assistant_agent_dependencies(self, agent: ExternalAgent | AssistantAgent) -> ExternalAgent | AssistantAgent:
|
315
|
+
agent_dict = agent.model_dump()
|
316
|
+
|
317
|
+
if agent_dict.get("app_id") or agent.config.model_dump().get("app_id"):
|
318
|
+
agent = self.dereference_app_id(agent)
|
319
|
+
|
320
|
+
return agent
|
321
|
+
|
322
|
+
def dereference_agent_dependencies(self, agent: Agent ) -> Agent | ExternalAgent | AssistantAgent:
|
323
|
+
if isinstance(agent, Agent):
|
324
|
+
return self.dereference_native_agent_dependencies(agent)
|
325
|
+
if isinstance(agent, ExternalAgent) or isinstance(agent, AssistantAgent):
|
326
|
+
return self.dereference_external_or_assistant_agent_dependencies(agent)
|
327
|
+
|
328
|
+
|
329
|
+
def publish_or_update_agents(
|
330
|
+
self, agents: Iterable[Agent]
|
331
|
+
):
|
332
|
+
for agent in agents:
|
333
|
+
agent_name = agent.name
|
334
|
+
|
335
|
+
native_client = self.get_native_client()
|
336
|
+
external_client = self.get_external_client()
|
337
|
+
assistant_client = self.get_assistant_client()
|
338
|
+
|
339
|
+
existing_native_agents = native_client.get_draft_by_name(agent_name)
|
340
|
+
existing_native_agents = [Agent.model_validate(agent) for agent in existing_native_agents]
|
341
|
+
existing_external_clients = external_client.get_draft_by_name(agent_name)
|
342
|
+
existing_external_clients = [ExternalAgent.model_validate(agent) for agent in existing_external_clients]
|
343
|
+
existing_assistant_clients = assistant_client.get_draft_by_name(agent_name)
|
344
|
+
existing_assistant_clients = [AssistantAgent.model_validate(agent) for agent in existing_assistant_clients]
|
345
|
+
|
346
|
+
all_existing_agents = existing_external_clients + existing_native_agents + existing_assistant_clients
|
347
|
+
agent = self.dereference_agent_dependencies(agent)
|
348
|
+
|
349
|
+
agent_kind = agent.kind
|
350
|
+
|
351
|
+
if len(all_existing_agents) > 1:
|
352
|
+
logger.error(f"Multiple agents with the name '{agent_name}' found. Failed to update agent")
|
353
|
+
sys.exit(1)
|
354
|
+
|
355
|
+
if len(all_existing_agents) > 0:
|
356
|
+
existing_agent = all_existing_agents[0]
|
357
|
+
|
358
|
+
if agent_name == existing_agent.name:
|
359
|
+
if agent_kind != existing_agent.kind:
|
360
|
+
logger.error(f"An agent with the name '{agent_name}' already exists with a different kind. Failed to create agent")
|
361
|
+
sys.exit(1)
|
362
|
+
agent_id = existing_agent.id
|
363
|
+
self.update_agent(agent_id=agent_id, agent=agent)
|
364
|
+
else:
|
365
|
+
self.publish_agent(agent)
|
366
|
+
|
367
|
+
def publish_agent(self, agent: Agent, **kwargs) -> None:
|
368
|
+
if isinstance(agent, Agent):
|
369
|
+
self.get_native_client().create(agent.model_dump())
|
370
|
+
logger.info(f"Agent '{agent.name}' imported successfully")
|
371
|
+
if isinstance(agent, ExternalAgent):
|
372
|
+
self.get_external_client().create(agent.model_dump())
|
373
|
+
logger.info(f"External Agent '{agent.name}' imported successfully")
|
374
|
+
if isinstance(agent, AssistantAgent):
|
375
|
+
self.get_assistant_client().create(agent.model_dump(by_alias=True))
|
376
|
+
logger.info(f"Assistant Agent '{agent.name}' imported successfully")
|
377
|
+
|
378
|
+
def update_agent(
|
379
|
+
self, agent_id: str, agent: Agent, **kwargs
|
380
|
+
) -> None:
|
381
|
+
if isinstance(agent, Agent):
|
382
|
+
logger.info(f"Existing Agent '{agent.name}' found. Updating...")
|
383
|
+
self.get_native_client().update(agent_id, agent.model_dump())
|
384
|
+
logger.info(f"Agent '{agent.name}' updated successfully")
|
385
|
+
if isinstance(agent, ExternalAgent):
|
386
|
+
logger.info(f"Existing External Agent '{agent.name}' found. Updating...")
|
387
|
+
self.get_external_client().update(agent_id, agent.model_dump())
|
388
|
+
logger.info(f"External Agent '{agent.name}' updated successfully")
|
389
|
+
if isinstance(agent, AssistantAgent):
|
390
|
+
logger.info(f"Existing Assistant Agent '{agent.name}' found. Updating...")
|
391
|
+
self.get_assistant_client().update(agent_id, agent.model_dump(by_alias=True))
|
392
|
+
logger.info(f"Assistant Agent '{agent.name}' updated successfully")
|
393
|
+
|
394
|
+
@staticmethod
|
395
|
+
def persist_record(agent: Agent, **kwargs):
|
396
|
+
if "output_file" in kwargs and kwargs["output_file"] is not None:
|
397
|
+
agent.spec_version = SpecVersion.V1
|
398
|
+
agent.dump_spec(kwargs["output_file"])
|
399
|
+
|
400
|
+
def get_agent_tool_names(self, tool_ids: List[str]) -> List[str]:
|
401
|
+
"""Retrieve tool names for a given agent based on tool IDs."""
|
402
|
+
tool_client = self.get_tool_client()
|
403
|
+
tools = []
|
404
|
+
for tool_id in tool_ids:
|
405
|
+
try:
|
406
|
+
tool = tool_client.get_draft_by_id(tool_id)
|
407
|
+
tools.append(tool["name"])
|
408
|
+
except Exception as e:
|
409
|
+
logger.warning(f"Tool with ID {tool_id} not found. Returning Tool ID")
|
410
|
+
tools.append(tool_id)
|
411
|
+
return tools
|
412
|
+
|
413
|
+
def get_agent_collaborator_names(self, agent_ids: List[str]) -> List[str]:
|
414
|
+
"""Retrieve collaborator names for a given agent based on collaborator IDs."""
|
415
|
+
collaborator_client = self.get_native_client()
|
416
|
+
external_client = self.get_external_client()
|
417
|
+
assistant_client = self.get_assistant_client()
|
418
|
+
collaborators = []
|
419
|
+
|
420
|
+
for agent_id in agent_ids:
|
421
|
+
try:
|
422
|
+
# First try resolving from native agents
|
423
|
+
collaborator = collaborator_client.get_draft_by_id(agent_id)
|
424
|
+
if collaborator:
|
425
|
+
collaborators.append(collaborator["name"])
|
426
|
+
continue
|
427
|
+
except Exception:
|
428
|
+
pass
|
429
|
+
|
430
|
+
try:
|
431
|
+
# If not found in native, check external agents
|
432
|
+
external_collaborator = external_client.get_draft_by_id(agent_id)
|
433
|
+
if external_collaborator:
|
434
|
+
collaborators.append(external_collaborator["name"])
|
435
|
+
continue
|
436
|
+
except Exception:
|
437
|
+
pass
|
438
|
+
|
439
|
+
try:
|
440
|
+
# If not found in native or external, check assistant agents
|
441
|
+
assistant_collaborator = assistant_client.get_draft_by_id(agent_id)
|
442
|
+
if assistant_collaborator:
|
443
|
+
collaborators.append(assistant_collaborator["name"])
|
444
|
+
continue
|
445
|
+
except Exception:
|
446
|
+
pass
|
447
|
+
|
448
|
+
logger.warning(f"Collaborator with ID {agent_id} not found. Returning Collaborator ID")
|
449
|
+
collaborators.append(agent_id)
|
450
|
+
|
451
|
+
return collaborators
|
452
|
+
|
453
|
+
def get_agent_knowledge_base_names(self, knowlede_base_ids: List[str]) -> List[str]:
|
454
|
+
"""Retrieve knowledge base names for a given agent based on knowledge base IDs."""
|
455
|
+
client = self.get_knowledge_base_client()
|
456
|
+
knowledge_bases = []
|
457
|
+
for id in knowlede_base_ids:
|
458
|
+
try:
|
459
|
+
kb = client.get_by_id(id)
|
460
|
+
knowledge_bases.append(kb["name"])
|
461
|
+
except Exception as e:
|
462
|
+
logger.warning(f"Knowledge base with ID {id} not found. Returning Tool ID")
|
463
|
+
knowledge_bases.append(id)
|
464
|
+
return knowledge_bases
|
465
|
+
|
466
|
+
def list_agents(self, kind: AgentKind=None, verbose: bool=False):
|
467
|
+
if kind == AgentKind.NATIVE or kind is None:
|
468
|
+
response = self.get_native_client().get()
|
469
|
+
native_agents = [Agent.model_validate(agent) for agent in response]
|
470
|
+
|
471
|
+
if verbose:
|
472
|
+
agents_list = []
|
473
|
+
for agent in native_agents:
|
474
|
+
|
475
|
+
agents_list.append(json.loads(agent.dumps_spec()))
|
476
|
+
|
477
|
+
rich.print(rich.json.JSON(json.dumps(agents_list, indent=4)))
|
478
|
+
else:
|
479
|
+
native_table = rich.table.Table(
|
480
|
+
show_header=True,
|
481
|
+
header_style="bold white",
|
482
|
+
title="Agents",
|
483
|
+
show_lines=True
|
484
|
+
)
|
485
|
+
column_args = {
|
486
|
+
"Name": {},
|
487
|
+
"Description": {},
|
488
|
+
"LLM": {"overflow": "fold"},
|
489
|
+
"Style": {},
|
490
|
+
"Collaborators": {},
|
491
|
+
"Tools": {},
|
492
|
+
"Knowledge Base": {},
|
493
|
+
"ID": {},
|
494
|
+
}
|
495
|
+
for column in column_args:
|
496
|
+
native_table.add_column(column, **column_args[column])
|
497
|
+
|
498
|
+
for agent in native_agents:
|
499
|
+
tool_names = self.get_agent_tool_names(agent.tools)
|
500
|
+
knowledge_base_names = self.get_agent_knowledge_base_names(agent.knowledge_base)
|
501
|
+
collaborator_names = self.get_agent_collaborator_names(agent.collaborators)
|
502
|
+
|
503
|
+
native_table.add_row(
|
504
|
+
agent.name,
|
505
|
+
agent.description,
|
506
|
+
agent.llm,
|
507
|
+
agent.style,
|
508
|
+
", ".join(collaborator_names),
|
509
|
+
", ".join(tool_names),
|
510
|
+
", ".join(knowledge_base_names),
|
511
|
+
agent.id,
|
512
|
+
)
|
513
|
+
rich.print(native_table)
|
514
|
+
|
515
|
+
|
516
|
+
if kind == AgentKind.EXTERNAL or kind is None:
|
517
|
+
response = self.get_external_client().get()
|
518
|
+
|
519
|
+
external_agents = [ExternalAgent.model_validate(agent) for agent in response]
|
520
|
+
|
521
|
+
response_dict = {agent["id"]: agent for agent in response}
|
522
|
+
|
523
|
+
# Insert config values into config as config object is not retruned from api
|
524
|
+
for external_agent in external_agents:
|
525
|
+
if external_agent.id in response_dict:
|
526
|
+
response_data = response_dict[external_agent.id]
|
527
|
+
external_agent.config.enable_cot = response_data.get("enable_cot", external_agent.config.enable_cot)
|
528
|
+
external_agent.config.hidden = response_data.get("hidden", external_agent.config.hidden)
|
529
|
+
|
530
|
+
external_agents_list = []
|
531
|
+
if verbose:
|
532
|
+
for agent in external_agents:
|
533
|
+
external_agents_list.append(json.loads(agent.dumps_spec()))
|
534
|
+
rich.print(rich.json.JSON(json.dumps(external_agents_list, indent=4)))
|
535
|
+
else:
|
536
|
+
external_table = rich.table.Table(
|
537
|
+
show_header=True,
|
538
|
+
header_style="bold white",
|
539
|
+
title="External Agents",
|
540
|
+
show_lines=True
|
541
|
+
)
|
542
|
+
column_args = {
|
543
|
+
"Name": {},
|
544
|
+
"Title": {},
|
545
|
+
"Description": {},
|
546
|
+
"Tags": {},
|
547
|
+
"API URL": {"overflow": "fold"},
|
548
|
+
"Chat Params": {},
|
549
|
+
"Config": {},
|
550
|
+
"Nickname": {},
|
551
|
+
"App ID": {},
|
552
|
+
"ID": {}
|
553
|
+
}
|
554
|
+
|
555
|
+
for column in column_args:
|
556
|
+
external_table.add_column(column, **column_args[column])
|
557
|
+
|
558
|
+
for agent in external_agents:
|
559
|
+
connections_client = get_connections_client()
|
560
|
+
app_id = connections_client.get_draft_by_id(agent.connection_id)
|
561
|
+
|
562
|
+
external_table.add_row(
|
563
|
+
agent.name,
|
564
|
+
agent.title,
|
565
|
+
agent.description,
|
566
|
+
", ".join(agent.tags or []),
|
567
|
+
agent.api_url,
|
568
|
+
json.dumps(agent.chat_params),
|
569
|
+
str(agent.config),
|
570
|
+
agent.nickname,
|
571
|
+
app_id,
|
572
|
+
agent.id
|
573
|
+
)
|
574
|
+
rich.print(external_table)
|
575
|
+
|
576
|
+
if kind == AgentKind.ASSISTANT or kind is None:
|
577
|
+
response = self.get_assistant_client().get()
|
578
|
+
|
579
|
+
assistant_agents = [AssistantAgent.model_validate(agent) for agent in response]
|
580
|
+
|
581
|
+
response_dict = {agent["id"]: agent for agent in response}
|
582
|
+
|
583
|
+
# Insert config values into config as config object is not retruned from api
|
584
|
+
for assistant_agent in assistant_agents:
|
585
|
+
if assistant_agent.id in response_dict:
|
586
|
+
response_data = response_dict[assistant_agent.id]
|
587
|
+
assistant_agent.config.api_version = response_data.get("api_version", assistant_agent.config.api_version)
|
588
|
+
assistant_agent.config.assistant_id = response_data.get("assistant_id", assistant_agent.config.assistant_id)
|
589
|
+
assistant_agent.config.crn = response_data.get("crn", assistant_agent.config.crn)
|
590
|
+
assistant_agent.config.service_instance_url = response_data.get("service_instance_url", assistant_agent.config.service_instance_url)
|
591
|
+
assistant_agent.config.environment_id = response_data.get("environment_id", assistant_agent.config.environment_id)
|
592
|
+
assistant_agent.config.authorization_url = response_data.get("authorization_url", assistant_agent.config.authorization_url)
|
593
|
+
|
594
|
+
if verbose:
|
595
|
+
for agent in assistant_agents:
|
596
|
+
rich.print(agent.dumps_spec())
|
597
|
+
else:
|
598
|
+
assistants_table = rich.table.Table(
|
599
|
+
show_header=True,
|
600
|
+
header_style="bold white",
|
601
|
+
title="Assistant Agents",
|
602
|
+
show_lines=True)
|
603
|
+
column_args = {
|
604
|
+
"Name": {},
|
605
|
+
"Title": {},
|
606
|
+
"Description": {},
|
607
|
+
"Tags": {},
|
608
|
+
"Nickname": {},
|
609
|
+
"CRN": {},
|
610
|
+
"Instance URL": {},
|
611
|
+
"Assistant ID": {},
|
612
|
+
"Environment ID": {},
|
613
|
+
"ID": {}
|
614
|
+
}
|
615
|
+
|
616
|
+
for column in column_args:
|
617
|
+
assistants_table.add_column(column, **column_args[column])
|
618
|
+
|
619
|
+
for agent in assistant_agents:
|
620
|
+
assistants_table.add_row(
|
621
|
+
agent.name,
|
622
|
+
agent.title,
|
623
|
+
agent.description,
|
624
|
+
", ".join(agent.tags or []),
|
625
|
+
agent.nickname,
|
626
|
+
agent.config.crn,
|
627
|
+
agent.config.service_instance_url,
|
628
|
+
agent.config.assistant_id,
|
629
|
+
agent.config.environment_id,
|
630
|
+
agent.id
|
631
|
+
)
|
632
|
+
rich.print(assistants_table)
|
633
|
+
|
634
|
+
def remove_agent(self, name: str, kind: AgentKind):
|
635
|
+
try:
|
636
|
+
if kind == AgentKind.NATIVE:
|
637
|
+
client = self.get_native_client()
|
638
|
+
elif kind == AgentKind.EXTERNAL:
|
639
|
+
client = self.get_external_client()
|
640
|
+
elif kind == AgentKind.ASSISTANT:
|
641
|
+
client = self.get_assistant_client()
|
642
|
+
else:
|
643
|
+
raise ValueError("'kind' must be 'native'")
|
644
|
+
|
645
|
+
draft_agents = client.get_draft_by_name(name)
|
646
|
+
if len(draft_agents) > 1:
|
647
|
+
logger.error(f"Multiple '{kind}' agents found with name '{name}'. Failed to delete agent")
|
648
|
+
sys.exit(1)
|
649
|
+
if len(draft_agents) > 0:
|
650
|
+
draft_agent = draft_agents[0]
|
651
|
+
agent_id = draft_agent.get("id")
|
652
|
+
client.delete(agent_id=agent_id)
|
653
|
+
|
654
|
+
logger.info(f"Successfully removed agent {name}")
|
655
|
+
else:
|
656
|
+
logger.warning(f"No agent named '{name}' found")
|
657
|
+
except requests.HTTPError as e:
|
658
|
+
logger.error(e.response.text)
|
659
|
+
exit(1)
|
660
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import typer
|
2
|
+
from ibm_watsonx_orchestrate.cli.commands.channels import channels_controller
|
3
|
+
from ibm_watsonx_orchestrate.cli.commands.channels.webchat.channels_webchat_command import channel_webchat
|
4
|
+
|
5
|
+
channel_app = typer.Typer(no_args_is_help=True)
|
6
|
+
|
7
|
+
channel_app.add_typer(
|
8
|
+
channel_webchat,
|
9
|
+
name="webchat",
|
10
|
+
help="Integrate with the Webchat Channel, for example exporting an embeddable code snippet can be achieved with the command 'orchestrate channel webchat embed --agent-name=some_agent --env=live'."
|
11
|
+
)
|
12
|
+
|
13
|
+
@channel_app.command(name="list", help="Lists the current supported Channels. A Channel refers to the different platforms you can embed your assistant into, such as web chat: orchestrate channel webchat embed --agent-name=some_agent --env=live")
|
14
|
+
def list_channel():
|
15
|
+
channels_controller.list_channels()
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from ibm_watsonx_orchestrate.cli.commands.channels.types import ChannelType
|
2
|
+
import rich
|
3
|
+
import rich.table
|
4
|
+
|
5
|
+
def list_channels():
|
6
|
+
table = rich.table.Table(show_header=True, header_style="bold white", show_lines=True)
|
7
|
+
columns = ["Channel"]
|
8
|
+
for col in columns:
|
9
|
+
table.add_column(col)
|
10
|
+
|
11
|
+
for channel in ChannelType.__members__.values():
|
12
|
+
|
13
|
+
table.add_row(channel)
|
14
|
+
|
15
|
+
console = rich.console.Console()
|
16
|
+
console.print(table)
|