ibm-watsonx-orchestrate 1.6.3__py3-none-any.whl → 1.6.4__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 +2 -1
- ibm_watsonx_orchestrate/agent_builder/agents/agent.py +3 -3
- ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +3 -2
- ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +3 -2
- ibm_watsonx_orchestrate/agent_builder/agents/types.py +38 -9
- ibm_watsonx_orchestrate/agent_builder/connections/connections.py +4 -3
- ibm_watsonx_orchestrate/agent_builder/connections/types.py +14 -2
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +1 -22
- ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +1 -17
- ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +2 -1
- ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +75 -24
- ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +136 -92
- ibm_watsonx_orchestrate/agent_builder/tools/types.py +17 -11
- ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +7 -7
- ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +7 -6
- ibm_watsonx_orchestrate/cli/commands/channels/types.py +3 -2
- ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +1 -2
- ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +14 -6
- ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +6 -8
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_command.py +65 -0
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +368 -0
- ibm_watsonx_orchestrate/cli/commands/copilot/copilot_server_controller.py +170 -0
- ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +5 -5
- ibm_watsonx_orchestrate/cli/commands/environment/types.py +2 -0
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +102 -37
- ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +20 -2
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +0 -18
- ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +36 -20
- ibm_watsonx_orchestrate/cli/commands/models/models_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/server/server_command.py +94 -36
- ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +1 -1
- ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +11 -4
- ibm_watsonx_orchestrate/cli/config.py +3 -3
- ibm_watsonx_orchestrate/cli/init_helper.py +10 -1
- ibm_watsonx_orchestrate/cli/main.py +5 -0
- ibm_watsonx_orchestrate/client/base_api_client.py +12 -0
- ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +67 -0
- ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +1 -1
- ibm_watsonx_orchestrate/client/local_service_instance.py +3 -1
- ibm_watsonx_orchestrate/client/service_instance.py +33 -7
- ibm_watsonx_orchestrate/client/utils.py +15 -13
- ibm_watsonx_orchestrate/docker/compose-lite.yml +198 -6
- ibm_watsonx_orchestrate/docker/default.env +36 -12
- ibm_watsonx_orchestrate/flow_builder/flows/__init__.py +9 -4
- ibm_watsonx_orchestrate/flow_builder/flows/decorators.py +4 -2
- ibm_watsonx_orchestrate/flow_builder/flows/events.py +10 -9
- ibm_watsonx_orchestrate/flow_builder/flows/flow.py +131 -20
- ibm_watsonx_orchestrate/flow_builder/node.py +18 -1
- ibm_watsonx_orchestrate/flow_builder/types.py +271 -16
- ibm_watsonx_orchestrate/flow_builder/utils.py +120 -6
- ibm_watsonx_orchestrate/utils/exceptions.py +23 -0
- {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/METADATA +3 -7
- {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/RECORD +56 -53
- ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py +0 -149
- ibm_watsonx_orchestrate/flow_builder/resources/flow_status.openapi.yml +0 -66
- {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/WHEEL +0 -0
- {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/entry_points.txt +0 -0
- {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/licenses/LICENSE +0 -0
@@ -33,6 +33,21 @@ logger = logging.getLogger(__name__)
|
|
33
33
|
server_app = typer.Typer(no_args_is_help=True)
|
34
34
|
|
35
35
|
|
36
|
+
_ALWAYS_UNSET: set[str] = {
|
37
|
+
"WO_API_KEY",
|
38
|
+
"WO_INSTANCE",
|
39
|
+
"DOCKER_IAM_KEY",
|
40
|
+
"WO_DEVELOPER_EDITION_SOURCE",
|
41
|
+
"WATSONX_SPACE_ID",
|
42
|
+
"WATSONX_APIKEY",
|
43
|
+
"WO_USERNAME",
|
44
|
+
"WO_PASSWORD",
|
45
|
+
}
|
46
|
+
|
47
|
+
def define_saas_wdu_runtime(value: str = "none") -> None:
|
48
|
+
cfg = Config()
|
49
|
+
cfg.write(USER_ENV_CACHE_HEADER,"SAAS_WDU_RUNTIME",value)
|
50
|
+
|
36
51
|
def ensure_docker_installed() -> None:
|
37
52
|
try:
|
38
53
|
subprocess.run(["docker", "--version"], check=True, capture_output=True)
|
@@ -261,6 +276,13 @@ def _check_exclusive_observibility(langfuse_enabled: bool, ibm_tele_enabled: boo
|
|
261
276
|
return False
|
262
277
|
return True
|
263
278
|
|
279
|
+
def _prepare_clean_env(env_file: Path) -> None:
|
280
|
+
"""Remove env vars so terminal definitions don't override"""
|
281
|
+
keys_from_file = set(dotenv_values(str(env_file)).keys())
|
282
|
+
keys_to_unset = keys_from_file | _ALWAYS_UNSET
|
283
|
+
for key in keys_to_unset:
|
284
|
+
os.environ.pop(key, None)
|
285
|
+
|
264
286
|
def write_merged_env_file(merged_env: dict) -> Path:
|
265
287
|
tmp = tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".env")
|
266
288
|
with tmp:
|
@@ -293,7 +315,8 @@ NON_SECRET_ENV_ITEMS = {
|
|
293
315
|
"WO_INSTANCE",
|
294
316
|
"USE_SAAS_ML_TOOLS_RUNTIME",
|
295
317
|
"AUTHORIZATION_URL",
|
296
|
-
"OPENSOURCE_REGISTRY_PROXY"
|
318
|
+
"OPENSOURCE_REGISTRY_PROXY",
|
319
|
+
"SAAS_WDU_RUNTIME"
|
297
320
|
}
|
298
321
|
def persist_user_env(env: dict, include_secrets: bool = False) -> None:
|
299
322
|
if include_secrets:
|
@@ -313,10 +336,10 @@ def get_persisted_user_env() -> dict | None:
|
|
313
336
|
user_env = cfg.get(USER_ENV_CACHE_HEADER) if cfg.get(USER_ENV_CACHE_HEADER) else None
|
314
337
|
return user_env
|
315
338
|
|
316
|
-
|
317
|
-
def run_compose_lite(final_env_file: Path, experimental_with_langfuse=False, experimental_with_ibm_telemetry=False) -> None:
|
339
|
+
def run_compose_lite(final_env_file: Path, experimental_with_langfuse=False, experimental_with_ibm_telemetry=False, with_doc_processing=False) -> None:
|
318
340
|
compose_path = get_compose_file()
|
319
341
|
compose_command = ensure_docker_compose_installed()
|
342
|
+
_prepare_clean_env(final_env_file)
|
320
343
|
db_tag = read_env_file(final_env_file).get('DBTAG', None)
|
321
344
|
logger.info(f"Detected architecture: {platform.machine()}, using DBTAG: {db_tag}")
|
322
345
|
|
@@ -341,19 +364,17 @@ def run_compose_lite(final_env_file: Path, experimental_with_langfuse=False, exp
|
|
341
364
|
|
342
365
|
|
343
366
|
# Step 2: Start all remaining services (except DB)
|
367
|
+
profiles = []
|
344
368
|
if experimental_with_langfuse:
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
command = compose_command + [
|
351
|
-
'--profile',
|
352
|
-
'ibm-telemetry'
|
353
|
-
]
|
354
|
-
else:
|
355
|
-
command = compose_command
|
369
|
+
profiles.append("langfuse")
|
370
|
+
if experimental_with_ibm_telemetry:
|
371
|
+
profiles.append("ibm-telemetry")
|
372
|
+
if with_doc_processing:
|
373
|
+
profiles.append("docproc")
|
356
374
|
|
375
|
+
command = compose_command[:]
|
376
|
+
for profile in profiles:
|
377
|
+
command += ["--profile", profile]
|
357
378
|
|
358
379
|
command += [
|
359
380
|
"-f", str(compose_path),
|
@@ -361,6 +382,8 @@ def run_compose_lite(final_env_file: Path, experimental_with_langfuse=False, exp
|
|
361
382
|
"up",
|
362
383
|
"--scale",
|
363
384
|
"ui=0",
|
385
|
+
"--scale",
|
386
|
+
"cpe=0",
|
364
387
|
"-d",
|
365
388
|
"--remove-orphans",
|
366
389
|
]
|
@@ -431,6 +454,7 @@ def wait_for_wxo_ui_health_check(timeout_seconds=45, interval_seconds=2):
|
|
431
454
|
def run_compose_lite_ui(user_env_file: Path) -> bool:
|
432
455
|
compose_path = get_compose_file()
|
433
456
|
compose_command = ensure_docker_compose_installed()
|
457
|
+
_prepare_clean_env(user_env_file)
|
434
458
|
ensure_docker_installed()
|
435
459
|
|
436
460
|
default_env = read_env_file(get_default_env_file())
|
@@ -525,6 +549,7 @@ def run_compose_lite_ui(user_env_file: Path) -> bool:
|
|
525
549
|
def run_compose_lite_down_ui(user_env_file: Path, is_reset: bool = False) -> None:
|
526
550
|
compose_path = get_compose_file()
|
527
551
|
compose_command = ensure_docker_compose_installed()
|
552
|
+
_prepare_clean_env(user_env_file)
|
528
553
|
|
529
554
|
|
530
555
|
ensure_docker_installed()
|
@@ -568,6 +593,7 @@ def run_compose_lite_down_ui(user_env_file: Path, is_reset: bool = False) -> Non
|
|
568
593
|
def run_compose_lite_down(final_env_file: Path, is_reset: bool = False) -> None:
|
569
594
|
compose_path = get_compose_file()
|
570
595
|
compose_command = ensure_docker_compose_installed()
|
596
|
+
_prepare_clean_env(final_env_file)
|
571
597
|
|
572
598
|
command = compose_command + [
|
573
599
|
'--profile', '*',
|
@@ -600,6 +626,7 @@ def run_compose_lite_down(final_env_file: Path, is_reset: bool = False) -> None:
|
|
600
626
|
def run_compose_lite_logs(final_env_file: Path, is_reset: bool = False) -> None:
|
601
627
|
compose_path = get_compose_file()
|
602
628
|
compose_command = ensure_docker_compose_installed()
|
629
|
+
_prepare_clean_env(final_env_file)
|
603
630
|
|
604
631
|
command = compose_command + [
|
605
632
|
"-f", str(compose_path),
|
@@ -655,63 +682,63 @@ def confirm_accepts_license_agreement(accepts_by_argument: bool):
|
|
655
682
|
def auto_configure_callback_ip(merged_env_dict: dict) -> dict:
|
656
683
|
"""
|
657
684
|
Automatically detect and configure CALLBACK_HOST_URL if it's empty.
|
658
|
-
|
685
|
+
|
659
686
|
Args:
|
660
687
|
merged_env_dict: The merged environment dictionary
|
661
|
-
|
688
|
+
|
662
689
|
Returns:
|
663
690
|
Updated environment dictionary with CALLBACK_HOST_URL set
|
664
691
|
"""
|
665
692
|
callback_url = merged_env_dict.get('CALLBACK_HOST_URL', '').strip()
|
666
|
-
|
693
|
+
|
667
694
|
# Only auto-configure if CALLBACK_HOST_URL is empty
|
668
695
|
if not callback_url:
|
669
696
|
logger.info("Auto-detecting local IP address for async tool callbacks...")
|
670
|
-
|
697
|
+
|
671
698
|
system = platform.system()
|
672
699
|
ip = None
|
673
|
-
|
700
|
+
|
674
701
|
try:
|
675
702
|
if system in ("Linux", "Darwin"):
|
676
703
|
result = subprocess.run(["ifconfig"], capture_output=True, text=True, check=True)
|
677
704
|
lines = result.stdout.splitlines()
|
678
|
-
|
705
|
+
|
679
706
|
for line in lines:
|
680
707
|
line = line.strip()
|
681
708
|
# Unix ifconfig output format: "inet 192.168.1.100 netmask 0xffffff00 broadcast 192.168.1.255"
|
682
709
|
if line.startswith("inet ") and "127.0.0.1" not in line:
|
683
710
|
candidate_ip = line.split()[1]
|
684
711
|
# Validate IP is not loopback or link-local
|
685
|
-
if (candidate_ip and
|
686
|
-
not candidate_ip.startswith("127.") and
|
712
|
+
if (candidate_ip and
|
713
|
+
not candidate_ip.startswith("127.") and
|
687
714
|
not candidate_ip.startswith("169.254")):
|
688
715
|
ip = candidate_ip
|
689
716
|
break
|
690
|
-
|
717
|
+
|
691
718
|
elif system == "Windows":
|
692
719
|
result = subprocess.run(["ipconfig"], capture_output=True, text=True, check=True)
|
693
720
|
lines = result.stdout.splitlines()
|
694
|
-
|
721
|
+
|
695
722
|
for line in lines:
|
696
723
|
line = line.strip()
|
697
724
|
# Windows ipconfig output format: " IPv4 Address. . . . . . . . . . . : 192.168.1.100"
|
698
725
|
if "IPv4 Address" in line and ":" in line:
|
699
726
|
candidate_ip = line.split(":")[-1].strip()
|
700
727
|
# Validate IP is not loopback or link-local
|
701
|
-
if (candidate_ip and
|
702
|
-
not candidate_ip.startswith("127.") and
|
728
|
+
if (candidate_ip and
|
729
|
+
not candidate_ip.startswith("127.") and
|
703
730
|
not candidate_ip.startswith("169.254")):
|
704
731
|
ip = candidate_ip
|
705
732
|
break
|
706
|
-
|
733
|
+
|
707
734
|
else:
|
708
735
|
logger.warning(f"Unsupported platform: {system}")
|
709
736
|
ip = None
|
710
|
-
|
737
|
+
|
711
738
|
except Exception as e:
|
712
739
|
logger.debug(f"IP detection failed on {system}: {e}")
|
713
740
|
ip = None
|
714
|
-
|
741
|
+
|
715
742
|
if ip:
|
716
743
|
callback_url = f"http://{ip}:4321"
|
717
744
|
merged_env_dict['CALLBACK_HOST_URL'] = callback_url
|
@@ -724,7 +751,7 @@ def auto_configure_callback_ip(merged_env_dict: dict) -> dict:
|
|
724
751
|
logger.info("For external tools, consider using ngrok or similar tunneling service.")
|
725
752
|
else:
|
726
753
|
logger.info(f"Using existing CALLBACK_HOST_URL: {callback_url}")
|
727
|
-
|
754
|
+
|
728
755
|
return merged_env_dict
|
729
756
|
|
730
757
|
@server_app.command(name="start")
|
@@ -755,9 +782,16 @@ def server_start(
|
|
755
782
|
"--accept-terms-and-conditions",
|
756
783
|
help="By providing this flag you accept the terms and conditions outlined in the logs on server start."
|
757
784
|
),
|
785
|
+
with_doc_processing: bool = typer.Option(
|
786
|
+
False,
|
787
|
+
'--with-doc-processing', '-d',
|
788
|
+
help='Enable IBM Document Processing to extract information from your business documents. Enabling this activates the Watson Document Understanding service.'
|
789
|
+
),
|
758
790
|
):
|
759
791
|
confirm_accepts_license_agreement(accept_terms_and_conditions)
|
760
792
|
|
793
|
+
define_saas_wdu_runtime()
|
794
|
+
|
761
795
|
if user_env_file and not Path(user_env_file).exists():
|
762
796
|
logger.error(f"Error: The specified environment file '{user_env_file}' does not exist.")
|
763
797
|
sys.exit(1)
|
@@ -789,10 +823,14 @@ def server_start(
|
|
789
823
|
logger.error("Please select either langfuse or ibm telemetry for observability not both")
|
790
824
|
sys.exit(1)
|
791
825
|
|
792
|
-
# Add LANGFUSE_ENABLED into the merged_env_dict, for tempus to pick up.
|
826
|
+
# Add LANGFUSE_ENABLED and DOCPROC_ENABLED into the merged_env_dict, for tempus to pick up.
|
793
827
|
if experimental_with_langfuse:
|
794
828
|
merged_env_dict['LANGFUSE_ENABLED'] = 'true'
|
795
|
-
|
829
|
+
|
830
|
+
if with_doc_processing:
|
831
|
+
merged_env_dict['DOCPROC_ENABLED'] = 'true'
|
832
|
+
define_saas_wdu_runtime("local")
|
833
|
+
|
796
834
|
if experimental_with_ibm_telemetry:
|
797
835
|
merged_env_dict['USE_IBM_TELEMETRY'] = 'true'
|
798
836
|
|
@@ -806,10 +844,12 @@ def server_start(
|
|
806
844
|
|
807
845
|
|
808
846
|
final_env_file = write_merged_env_file(merged_env_dict)
|
809
|
-
run_compose_lite(final_env_file=final_env_file,
|
810
|
-
experimental_with_langfuse=experimental_with_langfuse,
|
811
|
-
experimental_with_ibm_telemetry=experimental_with_ibm_telemetry)
|
812
847
|
|
848
|
+
run_compose_lite(final_env_file=final_env_file,
|
849
|
+
experimental_with_langfuse=experimental_with_langfuse,
|
850
|
+
experimental_with_ibm_telemetry=experimental_with_ibm_telemetry,
|
851
|
+
with_doc_processing=with_doc_processing)
|
852
|
+
|
813
853
|
run_db_migration()
|
814
854
|
|
815
855
|
logger.info("Waiting for orchestrate server to be fully initialized and ready...")
|
@@ -836,6 +876,8 @@ def server_start(
|
|
836
876
|
|
837
877
|
if experimental_with_langfuse:
|
838
878
|
logger.info(f"You can access the observability platform Langfuse at http://localhost:3010, username: orchestrate@ibm.com, password: orchestrate")
|
879
|
+
if with_doc_processing:
|
880
|
+
logger.info(f"Document processing in Flows (Public Preview) has been enabled.")
|
839
881
|
|
840
882
|
@server_app.command(name="stop")
|
841
883
|
def server_stop(
|
@@ -845,6 +887,7 @@ def server_stop(
|
|
845
887
|
help="Path to a .env file that overrides default.env. Then environment variables override both."
|
846
888
|
)
|
847
889
|
):
|
890
|
+
|
848
891
|
ensure_docker_installed()
|
849
892
|
default_env_path = get_default_env_file()
|
850
893
|
merged_env_dict = merge_env(
|
@@ -901,9 +944,24 @@ def server_logs(
|
|
901
944
|
def run_db_migration() -> None:
|
902
945
|
compose_path = get_compose_file()
|
903
946
|
compose_command = ensure_docker_compose_installed()
|
947
|
+
default_env_path = get_default_env_file()
|
948
|
+
merged_env_dict = merge_env(default_env_path, user_env_path=None)
|
949
|
+
merged_env_dict['WATSONX_SPACE_ID']='X'
|
950
|
+
merged_env_dict['WATSONX_APIKEY']='X'
|
951
|
+
merged_env_dict['WXAI_API_KEY'] = ''
|
952
|
+
merged_env_dict['ASSISTANT_EMBEDDINGS_API_KEY'] = ''
|
953
|
+
merged_env_dict['ASSISTANT_LLM_SPACE_ID'] = ''
|
954
|
+
merged_env_dict['ROUTING_LLM_SPACE_ID'] = ''
|
955
|
+
merged_env_dict['USE_SAAS_ML_TOOLS_RUNTIME'] = ''
|
956
|
+
merged_env_dict['BAM_API_KEY'] = ''
|
957
|
+
merged_env_dict['ASSISTANT_EMBEDDINGS_SPACE_ID'] = ''
|
958
|
+
merged_env_dict['ROUTING_LLM_API_KEY'] = ''
|
959
|
+
merged_env_dict['ASSISTANT_LLM_API_KEY'] = ''
|
960
|
+
final_env_file = write_merged_env_file(merged_env_dict)
|
904
961
|
|
905
962
|
command = compose_command + [
|
906
963
|
"-f", str(compose_path),
|
964
|
+
"--env-file", str(final_env_file),
|
907
965
|
"exec",
|
908
966
|
"wxo-server-db",
|
909
967
|
"bash",
|
@@ -943,4 +1001,4 @@ def run_db_migration() -> None:
|
|
943
1001
|
sys.exit(1)
|
944
1002
|
|
945
1003
|
if __name__ == "__main__":
|
946
|
-
server_app()
|
1004
|
+
server_app()
|
@@ -47,7 +47,7 @@ def import_toolkit(
|
|
47
47
|
] = None,
|
48
48
|
tools: Annotated[
|
49
49
|
Optional[str],
|
50
|
-
typer.Option("--tools", "-t", help="Comma-separated list of tools to import. Or you can use
|
50
|
+
typer.Option("--tools", "-t", help="Comma-separated list of tools to import. Or you can use \"*\" to use all tools"),
|
51
51
|
] = None,
|
52
52
|
app_id: Annotated[
|
53
53
|
List[str],
|
@@ -41,7 +41,7 @@ from ibm_watsonx_orchestrate.client.connections import get_connections_client, g
|
|
41
41
|
from ibm_watsonx_orchestrate.client.utils import instantiate_client, is_local_dev
|
42
42
|
from ibm_watsonx_orchestrate.flow_builder.utils import import_flow_support_tools
|
43
43
|
from ibm_watsonx_orchestrate.utils.utils import sanatize_app_id
|
44
|
-
from ibm_watsonx_orchestrate.
|
44
|
+
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
45
45
|
|
46
46
|
from ibm_watsonx_orchestrate import __version__
|
47
47
|
|
@@ -400,7 +400,14 @@ The [bold]flow tool[/bold] is being imported from [green]`{file}`[/green].
|
|
400
400
|
|
401
401
|
[bold cyan]Additional information:[/bold cyan]
|
402
402
|
|
403
|
-
- The [bold green]Get flow status[/bold green] tool is being imported to support flow tools. This tool can query the status of a flow tool instance.
|
403
|
+
- The [bold green]Get flow status[/bold green] tool is being imported to support flow tools. This tool can query the status of a flow tool instance. You can add it to your agent using the UI or including the following tool name in your agent definition: [green]i__get_flow_status_intrinsic_tool__[/green].
|
404
|
+
|
405
|
+
[bold cyan]Experimental Features - Scheduling Flows and Agents: [/bold cyan]
|
406
|
+
- You can now schedule any Flows to be run on a later time. Just include the [bold green]"schedulable=True"[/bold green] attribute in the @flow decorator.
|
407
|
+
- Once enabled, you can schedule a flow by saying something like: [bold green]Can you schedule the flow <flow_name> to run everyday at 7am EST for 3 times?[/bold green]
|
408
|
+
- To schedule an agent, see the example in [bold green]examples/flow_builder/agent_scheduler[/bold green]. Use that to import the [bold green]agent_run[/bold green] tool to your agent.
|
409
|
+
- Use [bold green]agent_run[/bold green] tool to schedule an agent. For example: [bold green]Can you schedule the agent <agent_name> to run every weekday at 8am UK time?[/bold green]
|
410
|
+
- In scheduling, it is important to mention timezone or UTC time (also known as Greenwich Mean Time or Coordinated Universal Time) will be used.
|
404
411
|
|
405
412
|
"""
|
406
413
|
|
@@ -477,7 +484,7 @@ The [bold]flow tool[/bold] is being imported from [green]`{file}`[/green].
|
|
477
484
|
permission="read_only",
|
478
485
|
flow_model=model)
|
479
486
|
|
480
|
-
tools = import_flow_support_tools()
|
487
|
+
tools = import_flow_support_tools(model=model)
|
481
488
|
|
482
489
|
tools.append(tool)
|
483
490
|
|
@@ -554,7 +561,7 @@ class ToolsController:
|
|
554
561
|
tools = []
|
555
562
|
logger.warning("Skill Import not implemented yet")
|
556
563
|
case _:
|
557
|
-
raise
|
564
|
+
raise BadRequest("Invalid kind selected")
|
558
565
|
|
559
566
|
for tool in tools:
|
560
567
|
yield tool
|
@@ -6,6 +6,7 @@ from copy import deepcopy
|
|
6
6
|
from ibm_watsonx_orchestrate.cli.commands.tools.types import RegistryType
|
7
7
|
from ibm_watsonx_orchestrate.utils.utils import yaml_safe_load
|
8
8
|
from enum import Enum
|
9
|
+
from ibm_watsonx_orchestrate.utils.exceptions import BadRequest
|
9
10
|
|
10
11
|
# Section Headers
|
11
12
|
AUTH_SECTION_HEADER = "auth"
|
@@ -82,7 +83,6 @@ def _check_if_default_config_file(folder, file):
|
|
82
83
|
def _check_if_auth_config_file(folder, file):
|
83
84
|
return folder == AUTH_CONFIG_FILE_FOLDER and file == AUTH_CONFIG_FILE
|
84
85
|
|
85
|
-
|
86
86
|
def clear_protected_env_credentials_token():
|
87
87
|
auth_cfg = Config(config_file_folder=AUTH_CONFIG_FILE_FOLDER, config_file=AUTH_CONFIG_FILE)
|
88
88
|
auth_cfg.delete(AUTH_SECTION_HEADER, PROTECTED_ENV_NAME, AUTH_MCSP_TOKEN_OPT)
|
@@ -91,7 +91,7 @@ def clear_protected_env_credentials_token():
|
|
91
91
|
class ConfigFileTypes(str, Enum):
|
92
92
|
AUTH = 'auth'
|
93
93
|
CONFIG = 'config'
|
94
|
-
|
94
|
+
DOCPROC_FEATURE_CONF= 'docproc_feature'
|
95
95
|
|
96
96
|
class Config:
|
97
97
|
|
@@ -209,7 +209,7 @@ class Config:
|
|
209
209
|
as keys to access deeper sections of the config and then deleting the last specified key.
|
210
210
|
"""
|
211
211
|
if len(args) < 1:
|
212
|
-
raise
|
212
|
+
raise BadRequest("Config.delete() requires at least one positional argument")
|
213
213
|
|
214
214
|
config_data = {}
|
215
215
|
try:
|
@@ -4,6 +4,7 @@ from typing import Optional
|
|
4
4
|
from rich import print as pprint
|
5
5
|
from dotenv import dotenv_values
|
6
6
|
import typer
|
7
|
+
import sys
|
7
8
|
|
8
9
|
from ibm_watsonx_orchestrate.cli.config import Config, PYTHON_REGISTRY_HEADER, \
|
9
10
|
PYTHON_REGISTRY_TEST_PACKAGE_VERSION_OVERRIDE_OPT
|
@@ -29,7 +30,6 @@ def version_callback(checkVersion: bool=True):
|
|
29
30
|
|
30
31
|
raise typer.Exit()
|
31
32
|
|
32
|
-
|
33
33
|
|
34
34
|
def init_callback(
|
35
35
|
ctx: typer.Context,
|
@@ -38,6 +38,15 @@ def init_callback(
|
|
38
38
|
"--version",
|
39
39
|
help="Show the installed version of the ADK and Developer Edition Tags",
|
40
40
|
callback=version_callback
|
41
|
+
),
|
42
|
+
debug: Optional[bool] = typer.Option(
|
43
|
+
False,
|
44
|
+
"--debug",
|
45
|
+
help="Enable debug mode"
|
41
46
|
)
|
42
47
|
):
|
48
|
+
if debug:
|
49
|
+
sys.tracebacklimit = 40
|
50
|
+
else:
|
51
|
+
sys.tracebacklimit = 0
|
43
52
|
pass
|
@@ -1,4 +1,6 @@
|
|
1
|
+
|
1
2
|
import typer
|
3
|
+
import sys
|
2
4
|
|
3
5
|
from ibm_watsonx_orchestrate.cli.commands.connections.connections_command import connections_app
|
4
6
|
from ibm_watsonx_orchestrate.cli.commands.login.login_command import login_app
|
@@ -13,6 +15,7 @@ from ibm_watsonx_orchestrate.cli.commands.channels.channels_command import chann
|
|
13
15
|
from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_command import knowledge_bases_app
|
14
16
|
from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_command import toolkits_app
|
15
17
|
from ibm_watsonx_orchestrate.cli.commands.evaluations.evaluations_command import evaluation_app
|
18
|
+
from ibm_watsonx_orchestrate.cli.commands.copilot.copilot_command import copilot_app
|
16
19
|
from ibm_watsonx_orchestrate.cli.init_helper import init_callback
|
17
20
|
|
18
21
|
import urllib3
|
@@ -24,6 +27,7 @@ app = typer.Typer(
|
|
24
27
|
pretty_exceptions_enable=False,
|
25
28
|
callback=init_callback
|
26
29
|
)
|
30
|
+
|
27
31
|
app.add_typer(login_app)
|
28
32
|
app.add_typer(environment_app, name="env", help='Add, remove, or select the activate env other commands will interact with (either your local server or a production instance)')
|
29
33
|
app.add_typer(agents_app, name="agents", help='Interact with the agents in your active env')
|
@@ -36,6 +40,7 @@ app.add_typer(chat_app, name="chat", help='Launch the chat ui for your local Dev
|
|
36
40
|
app.add_typer(models_app, name="models", help='List the available large language models (llms) that can be used in your agent definitions')
|
37
41
|
app.add_typer(channel_app, name="channels", help="Configure channels where your agent can exist on (such as embedded webchat)")
|
38
42
|
app.add_typer(evaluation_app, name="evaluations", help='Evaluate the performance of your agents in your active env')
|
43
|
+
app.add_typer(copilot_app, name="copilot", help='Access AI powered assistance to help refine your agents')
|
39
44
|
app.add_typer(settings_app, name="settings", help='Configure the settings for your active env')
|
40
45
|
|
41
46
|
if __name__ == "__main__":
|
@@ -3,6 +3,7 @@ import json
|
|
3
3
|
import requests
|
4
4
|
from abc import ABC, abstractmethod
|
5
5
|
from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
|
6
|
+
from typing_extensions import List
|
6
7
|
|
7
8
|
|
8
9
|
class ClientAPIException(requests.HTTPError):
|
@@ -62,6 +63,17 @@ class BaseAPIClient:
|
|
62
63
|
self._check_response(response)
|
63
64
|
return response.json() if response.text else {}
|
64
65
|
|
66
|
+
def _post_nd_json(self, path: str, data: dict = None, files: dict = None) -> List[dict]:
|
67
|
+
url = f"{self.base_url}{path}"
|
68
|
+
response = requests.post(url, headers=self._get_headers(), json=data, files=files)
|
69
|
+
self._check_response(response)
|
70
|
+
|
71
|
+
res = []
|
72
|
+
if response.text:
|
73
|
+
for line in response.text.splitlines():
|
74
|
+
res.append(json.loads(line))
|
75
|
+
return res
|
76
|
+
|
65
77
|
def _post_form_data(self, path: str, data: dict = None, files: dict = None) -> dict:
|
66
78
|
url = f"{self.base_url}{path}"
|
67
79
|
# Use data argument instead of json so data is encoded as application/x-www-form-urlencoded
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from typing import Dict, Any
|
2
|
+
from uuid import uuid4
|
3
|
+
|
4
|
+
from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient
|
5
|
+
|
6
|
+
|
7
|
+
class CPEClient(BaseAPIClient):
|
8
|
+
"""
|
9
|
+
Client to handle CRUD operations for Conversational Prompt Engineering Service
|
10
|
+
"""
|
11
|
+
|
12
|
+
def __init__(self, *args, **kwargs):
|
13
|
+
self.chat_id = str(uuid4())
|
14
|
+
super().__init__(*args, **kwargs)
|
15
|
+
self.base_url = kwargs.get("base_url", self.base_url)
|
16
|
+
self.chat_model_name = 'llama-3-3-70b-instruct'
|
17
|
+
|
18
|
+
def _get_headers(self) -> dict:
|
19
|
+
return {
|
20
|
+
"chat_id": self.chat_id
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
def submit_pre_cpe_chat(self, user_message: str | None =None, tools: Dict[str, Any] = None, agents: Dict[str, Any] = None) -> dict:
|
25
|
+
payload = {
|
26
|
+
"message": user_message,
|
27
|
+
"tools": tools,
|
28
|
+
"agents": agents,
|
29
|
+
"chat_id": self.chat_id,
|
30
|
+
"chat_model_name": self.chat_model_name
|
31
|
+
}
|
32
|
+
|
33
|
+
response = self._post_nd_json("/wxo-cpe/create-agent", data=payload)
|
34
|
+
|
35
|
+
if response:
|
36
|
+
return response[-1]
|
37
|
+
|
38
|
+
|
39
|
+
def init_with_context(self, model: str | None = None, context_data: Dict[str, Any] = None) -> dict:
|
40
|
+
payload = {
|
41
|
+
"context_data": context_data,
|
42
|
+
"chat_id": self.chat_id
|
43
|
+
}
|
44
|
+
|
45
|
+
if model:
|
46
|
+
payload["target_model_name"] = model
|
47
|
+
|
48
|
+
response = self._post_nd_json("/wxo-cpe/init_cpe_from_wxo", data=payload)
|
49
|
+
|
50
|
+
if response:
|
51
|
+
return response[-1]
|
52
|
+
|
53
|
+
|
54
|
+
def invoke(self, prompt: str, model: str | None = None, context_data: Dict[str, Any] = None) -> dict:
|
55
|
+
payload = {
|
56
|
+
"prompt": prompt,
|
57
|
+
"context_data": context_data,
|
58
|
+
"chat_id": self.chat_id
|
59
|
+
}
|
60
|
+
|
61
|
+
if model:
|
62
|
+
payload["target_model_name"] = model
|
63
|
+
|
64
|
+
response = self._post_nd_json("/wxo-cpe/invoke", data=payload)
|
65
|
+
|
66
|
+
if response:
|
67
|
+
return response[-1]
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient
|
1
|
+
from ibm_watsonx_orchestrate.client.base_api_client import BaseAPIClient, ClientAPIException
|
2
2
|
import json
|
3
3
|
from typing_extensions import List
|
4
4
|
from ibm_watsonx_orchestrate.client.utils import is_local_dev
|
@@ -2,6 +2,8 @@ from ibm_watsonx_orchestrate.client.base_service_instance import BaseServiceInst
|
|
2
2
|
import logging
|
3
3
|
import requests
|
4
4
|
from ibm_watsonx_orchestrate.client.credentials import Credentials
|
5
|
+
import json
|
6
|
+
import base64
|
5
7
|
|
6
8
|
logger = logging.getLogger(__name__)
|
7
9
|
|
@@ -11,7 +13,7 @@ DEFAULT_TENANT = {
|
|
11
13
|
"tags": ["test"]
|
12
14
|
}
|
13
15
|
|
14
|
-
DEFAULT_USER =
|
16
|
+
DEFAULT_USER = json.loads(base64.b64decode('eyJ1c2VybmFtZSI6ICJ3eG8uYXJjaGVyQGlibS5jb20iLCJwYXNzd29yZCI6ICJ3YXRzb254In0='))
|
15
17
|
DEFAULT_LOCAL_SERVICE_URL = "http://localhost:4321"
|
16
18
|
DEFAULT_LOCAL_AUTH_ENDPOINT = f"{DEFAULT_LOCAL_SERVICE_URL}/api/v1/auth/token"
|
17
19
|
DEFAULT_LOCAL_TENANT_URL = f"{DEFAULT_LOCAL_SERVICE_URL}/api/v1/tenants"
|
@@ -6,6 +6,7 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
from ibm_cloud_sdk_core.authenticators import MCSPAuthenticator
|
9
|
+
from ibm_cloud_sdk_core.authenticators import MCSPV2Authenticator
|
9
10
|
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
|
10
11
|
from ibm_cloud_sdk_core.authenticators import CloudPakForDataAuthenticator
|
11
12
|
|
@@ -51,24 +52,47 @@ class ServiceInstance(BaseServiceInstance):
|
|
51
52
|
def _create_token(self) -> str:
|
52
53
|
if not self._credentials.auth_type:
|
53
54
|
if ".cloud.ibm.com" in self._credentials.url:
|
54
|
-
logger.warning("Using IBM IAM Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of '
|
55
|
+
logger.warning("Using IBM IAM Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'mcsp', 'mcsp_v1', 'mcsp_v2' or 'cpd' ")
|
55
56
|
return self._authenticate(EnvironmentAuthType.IBM_CLOUD_IAM)
|
56
57
|
elif is_cpd_env(self._credentials.url):
|
57
|
-
logger.warning("Using CPD Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam'
|
58
|
+
logger.warning("Using CPD Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam', 'mcsp', 'mcsp_v1' or 'mcsp_v2' ")
|
58
59
|
return self._authenticate(EnvironmentAuthType.CPD)
|
59
60
|
else:
|
60
|
-
logger.warning("Using MCSP Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam'
|
61
|
-
|
61
|
+
logger.warning("Using MCSP Auth Type. If this is incorrect please use the '--type' flag to explicitly choose one of 'ibm_iam', 'mcsp_v1', 'mcsp_v2' or 'cpd' ")
|
62
|
+
try:
|
63
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V1)
|
64
|
+
except:
|
65
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V2)
|
66
|
+
auth_type = self._credentials.auth_type.lower()
|
67
|
+
if auth_type == "mcsp":
|
68
|
+
try:
|
69
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V1)
|
70
|
+
except:
|
71
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V2)
|
72
|
+
elif auth_type == "mcsp_v1":
|
73
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V1)
|
74
|
+
elif auth_type == "mcsp_v2":
|
75
|
+
return self._authenticate(EnvironmentAuthType.MCSP_V2)
|
62
76
|
else:
|
63
|
-
return self._authenticate(
|
77
|
+
return self._authenticate(auth_type)
|
64
78
|
|
65
79
|
def _authenticate(self, auth_type: str) -> str:
|
66
80
|
"""Handles authentication based on the auth_type."""
|
67
81
|
try:
|
68
82
|
match auth_type:
|
69
|
-
case EnvironmentAuthType.MCSP:
|
83
|
+
case EnvironmentAuthType.MCSP | EnvironmentAuthType.MCSP_V1:
|
70
84
|
url = self._credentials.iam_url if self._credentials.iam_url is not None else "https://iam.platform.saas.ibm.com"
|
71
85
|
authenticator = MCSPAuthenticator(apikey=self._credentials.api_key, url=url)
|
86
|
+
case EnvironmentAuthType.MCSP_V2:
|
87
|
+
url = self._credentials.iam_url if self._credentials.iam_url is not None else "https://account-iam.platform.saas.ibm.com"
|
88
|
+
wxo_url = self._credentials.url
|
89
|
+
instance_id = wxo_url.split("instances/")[1]
|
90
|
+
authenticator = MCSPV2Authenticator(
|
91
|
+
apikey=self._credentials.api_key,
|
92
|
+
url=url,
|
93
|
+
scope_collection_type="services",
|
94
|
+
scope_id=instance_id
|
95
|
+
)
|
72
96
|
case EnvironmentAuthType.IBM_CLOUD_IAM:
|
73
97
|
authenticator = IAMAuthenticator(apikey=self._credentials.api_key, url=self._credentials.iam_url)
|
74
98
|
case EnvironmentAuthType.CPD:
|
@@ -100,8 +124,10 @@ class ServiceInstance(BaseServiceInstance):
|
|
100
124
|
raise ClientError(f"Unsupported authentication type: {auth_type}")
|
101
125
|
|
102
126
|
return authenticator.token_manager.get_token()
|
127
|
+
|
103
128
|
except Exception as e:
|
104
|
-
raise ClientError(f"Error getting {auth_type.upper()} Token",
|
129
|
+
raise ClientError(f"Error getting {auth_type.upper()} Token", logg_messages=False)
|
130
|
+
|
105
131
|
|
106
132
|
|
107
133
|
def _is_token_refresh_possible(self) -> bool:
|