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.
Files changed (58) hide show
  1. ibm_watsonx_orchestrate/__init__.py +2 -1
  2. ibm_watsonx_orchestrate/agent_builder/agents/agent.py +3 -3
  3. ibm_watsonx_orchestrate/agent_builder/agents/assistant_agent.py +3 -2
  4. ibm_watsonx_orchestrate/agent_builder/agents/external_agent.py +3 -2
  5. ibm_watsonx_orchestrate/agent_builder/agents/types.py +38 -9
  6. ibm_watsonx_orchestrate/agent_builder/connections/connections.py +4 -3
  7. ibm_watsonx_orchestrate/agent_builder/connections/types.py +14 -2
  8. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/knowledge_base_requests.py +1 -22
  9. ibm_watsonx_orchestrate/agent_builder/knowledge_bases/types.py +1 -17
  10. ibm_watsonx_orchestrate/agent_builder/tools/base_tool.py +2 -1
  11. ibm_watsonx_orchestrate/agent_builder/tools/openapi_tool.py +75 -24
  12. ibm_watsonx_orchestrate/agent_builder/tools/python_tool.py +136 -92
  13. ibm_watsonx_orchestrate/agent_builder/tools/types.py +17 -11
  14. ibm_watsonx_orchestrate/cli/commands/agents/agents_command.py +7 -7
  15. ibm_watsonx_orchestrate/cli/commands/agents/agents_controller.py +7 -6
  16. ibm_watsonx_orchestrate/cli/commands/channels/types.py +3 -2
  17. ibm_watsonx_orchestrate/cli/commands/channels/webchat/channels_webchat_controller.py +1 -2
  18. ibm_watsonx_orchestrate/cli/commands/connections/connections_command.py +14 -6
  19. ibm_watsonx_orchestrate/cli/commands/connections/connections_controller.py +6 -8
  20. ibm_watsonx_orchestrate/cli/commands/copilot/copilot_command.py +65 -0
  21. ibm_watsonx_orchestrate/cli/commands/copilot/copilot_controller.py +368 -0
  22. ibm_watsonx_orchestrate/cli/commands/copilot/copilot_server_controller.py +170 -0
  23. ibm_watsonx_orchestrate/cli/commands/environment/environment_controller.py +5 -5
  24. ibm_watsonx_orchestrate/cli/commands/environment/types.py +2 -0
  25. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_command.py +102 -37
  26. ibm_watsonx_orchestrate/cli/commands/evaluations/evaluations_controller.py +20 -2
  27. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_command.py +0 -18
  28. ibm_watsonx_orchestrate/cli/commands/knowledge_bases/knowledge_bases_controller.py +36 -20
  29. ibm_watsonx_orchestrate/cli/commands/models/models_command.py +1 -1
  30. ibm_watsonx_orchestrate/cli/commands/server/server_command.py +94 -36
  31. ibm_watsonx_orchestrate/cli/commands/toolkit/toolkit_command.py +1 -1
  32. ibm_watsonx_orchestrate/cli/commands/tools/tools_controller.py +11 -4
  33. ibm_watsonx_orchestrate/cli/config.py +3 -3
  34. ibm_watsonx_orchestrate/cli/init_helper.py +10 -1
  35. ibm_watsonx_orchestrate/cli/main.py +5 -0
  36. ibm_watsonx_orchestrate/client/base_api_client.py +12 -0
  37. ibm_watsonx_orchestrate/client/copilot/cpe/copilot_cpe_client.py +67 -0
  38. ibm_watsonx_orchestrate/client/knowledge_bases/knowledge_base_client.py +1 -1
  39. ibm_watsonx_orchestrate/client/local_service_instance.py +3 -1
  40. ibm_watsonx_orchestrate/client/service_instance.py +33 -7
  41. ibm_watsonx_orchestrate/client/utils.py +15 -13
  42. ibm_watsonx_orchestrate/docker/compose-lite.yml +198 -6
  43. ibm_watsonx_orchestrate/docker/default.env +36 -12
  44. ibm_watsonx_orchestrate/flow_builder/flows/__init__.py +9 -4
  45. ibm_watsonx_orchestrate/flow_builder/flows/decorators.py +4 -2
  46. ibm_watsonx_orchestrate/flow_builder/flows/events.py +10 -9
  47. ibm_watsonx_orchestrate/flow_builder/flows/flow.py +131 -20
  48. ibm_watsonx_orchestrate/flow_builder/node.py +18 -1
  49. ibm_watsonx_orchestrate/flow_builder/types.py +271 -16
  50. ibm_watsonx_orchestrate/flow_builder/utils.py +120 -6
  51. ibm_watsonx_orchestrate/utils/exceptions.py +23 -0
  52. {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/METADATA +3 -7
  53. {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/RECORD +56 -53
  54. ibm_watsonx_orchestrate/agent_builder/utils/pydantic_utils.py +0 -149
  55. ibm_watsonx_orchestrate/flow_builder/resources/flow_status.openapi.yml +0 -66
  56. {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/WHEEL +0 -0
  57. {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/entry_points.txt +0 -0
  58. {ibm_watsonx_orchestrate-1.6.3.dist-info → ibm_watsonx_orchestrate-1.6.4.dist-info}/licenses/LICENSE +0 -0
@@ -2,11 +2,12 @@ import json
2
2
  import logging
3
3
  import typer
4
4
  import os
5
- import yaml
6
5
  import csv
7
6
  import rich
8
7
  import sys
9
8
  import shutil
9
+ import tempfile
10
+ import random
10
11
 
11
12
  from rich.panel import Panel
12
13
  from pathlib import Path
@@ -16,23 +17,55 @@ from typing_extensions import Annotated
16
17
 
17
18
  from ibm_watsonx_orchestrate import __version__
18
19
  from ibm_watsonx_orchestrate.cli.commands.evaluations.evaluations_controller import EvaluationsController
20
+ from ibm_watsonx_orchestrate.cli.commands.agents.agents_controller import AgentsController
19
21
 
20
22
  logger = logging.getLogger(__name__)
21
23
 
22
24
  evaluation_app = typer.Typer(no_args_is_help=True)
23
25
 
26
+ def _native_agent_template():
27
+ return {
28
+ "spec_version": "v1",
29
+ "style": "default",
30
+ "llm": "watsonx/meta-llama/llama-3-405b-instruct",
31
+ "name": "",
32
+ "description": "Native agent for validating external agent",
33
+ "instructions": "Use the tools and external agent(s) provided to answer the user's question. If you do not have enough information to answer the question, say so. If you need more information, ask follow up questions.",
34
+ "collaborators": []
35
+ }
36
+
37
+ def _random_native_agent_name(external_agent_name):
38
+ """ Generate a native agent name in the following format to ensure uniqueness:
39
+
40
+ "external_agent_validation_{external_agent_name}_{random number}
41
+
42
+ So if the external agent name is, "QA_Agent", and the random number generated is, '100', the native agent name is:
43
+ "external_agent_validation_QA_Agent_100"
44
+
45
+ """
46
+ seed = 42
47
+ random.seed(seed)
48
+
49
+ return f"external_agent_validation_{external_agent_name}_{random.randint(0, 100)}"
50
+
24
51
  def read_env_file(env_path: Path|str) -> dict:
25
52
  return dotenv_values(str(env_path))
26
53
 
27
54
  def validate_watsonx_credentials(user_env_file: str) -> bool:
28
- required_keys = ["WATSONX_SPACE_ID", "WATSONX_APIKEY"]
55
+ required_sets = [
56
+ ["WATSONX_SPACE_ID", "WATSONX_APIKEY"],
57
+ ["WO_INSTANCE", "WO_API_KEY"]
58
+ ]
29
59
 
30
- if all(key in os.environ for key in required_keys):
60
+ def has_valid_keys(env: dict) -> bool:
61
+ return any(all(key in env for key in key_set) for key_set in required_sets)
62
+
63
+ if has_valid_keys(os.environ):
31
64
  logger.info("WatsonX credentials validated successfully.")
32
65
  return
33
66
 
34
67
  if user_env_file is None:
35
- logger.error("WatsonX credentials are not set. Please set WATSONX_SPACE_ID and WATSONX_APIKEY in your system environment variables or include them in your enviroment file and pass it with --env-file option.")
68
+ logger.error("WatsonX credentials are not set. Please set either WATSONX_SPACE_ID and WATSONX_APIKEY or WO_INSTANCE and WO_API_KEY in your system environment variables or include them in your environment file and pass it with --env-file option.")
36
69
  sys.exit(1)
37
70
 
38
71
  if not Path(user_env_file).exists():
@@ -41,11 +74,15 @@ def validate_watsonx_credentials(user_env_file: str) -> bool:
41
74
 
42
75
  user_env = read_env_file(user_env_file)
43
76
 
44
- if not all(key in user_env for key in required_keys):
45
- logger.error("Error: The environment file does not contain the required keys: WATSONX_SPACE_ID and WATSONX_APIKEY.")
77
+ if not has_valid_keys(user_env):
78
+ logger.error("Error: The environment file does not contain the required keys: either WATSONX_SPACE_ID and WATSONX_APIKEY or WO_INSTANCE and WO_API_KEY.")
46
79
  sys.exit(1)
47
80
 
48
- os.environ.update({key: user_env[key] for key in required_keys})
81
+ # Update os.environ with whichever set is present
82
+ for key_set in required_sets:
83
+ if all(key in user_env for key in key_set):
84
+ os.environ.update({key: user_env[key] for key in key_set})
85
+ break
49
86
  logger.info("WatsonX credentials validated successfully.")
50
87
 
51
88
  def read_csv(data_path: str, delimiter="\t"):
@@ -208,7 +245,7 @@ def validate_external(
208
245
  str,
209
246
  typer.Option(
210
247
  "--external-agent-config", "-ext",
211
- help="Path to the external agent yaml",
248
+ help="Path to the external agent json file",
212
249
 
213
250
  )
214
251
  ],
@@ -234,33 +271,65 @@ def validate_external(
234
271
  help="Path to a .env file that overrides default.env. Then environment variables override both."
235
272
  ),
236
273
  ] = None,
237
- agent_name: Annotated[
238
- str,
274
+ perf_test: Annotated[
275
+ bool,
239
276
  typer.Option(
240
- "--agent_name", "-a",
241
- help="Name of the native agent which has the external agent to test registered as a collaborater. See: https://developer.watson-orchestrate.ibm.com/agents/build_agent#native-agents)." \
242
- " If this parameter is pased, validation of the external agent is not run.",
243
- rich_help_panel="Parameters for Input Evaluation"
277
+ "--perf", "-p",
278
+ help="Performance test your external agent against the provide user stories.",
279
+ rich_help_panel="Parameters for Input Evaluation",
244
280
  )
245
- ] = None
281
+ ] = False
246
282
  ):
247
283
 
248
284
  validate_watsonx_credentials(user_env_file)
249
- Path(output_dir).mkdir(exist_ok=True)
250
- shutil.copy(data_path, os.path.join(output_dir, "input_sample.tsv"))
251
285
 
252
- if agent_name is not None:
253
- eval_dir = os.path.join(output_dir, "evaluation")
286
+ with open(external_agent_config, 'r') as f:
287
+ try:
288
+ external_agent_config = json.load(f)
289
+ except Exception:
290
+ rich.print(
291
+ f"[red]: Please provide a valid external agent spec in JSON format. See 'examples/evaluations/external_agent_validation/sample_external_agent_config.json' for an example."
292
+ )
293
+ sys.exit(1)
294
+
295
+ eval_dir = os.path.join(output_dir, "evaluations")
296
+ if perf_test:
254
297
  if os.path.exists(eval_dir):
255
298
  rich.print(f"[yellow]: found existing {eval_dir} in target directory. All content is removed.")
256
- shutil.rmtree(os.path.join(output_dir, "evaluation"))
257
- Path(eval_dir).mkdir(exist_ok=True)
299
+ shutil.rmtree(eval_dir)
300
+ Path(eval_dir).mkdir(exist_ok=True, parents=True)
258
301
  # save external agent config even though its not used for evaluation
259
302
  # it can help in later debugging customer agents
260
- with open(os.path.join(eval_dir, "external_agent_cfg.yaml"), "w+") as f:
261
- with open(external_agent_config, "r") as cfg:
262
- external_agent_config = yaml.safe_load(cfg)
263
- yaml.safe_dump(external_agent_config, f, indent=4)
303
+ with open(os.path.join(eval_dir, f"external_agent_cfg.json"), "w+") as f:
304
+ json.dump(external_agent_config, f, indent=4)
305
+
306
+ logger.info("Registering External Agent")
307
+ agent_controller = AgentsController()
308
+
309
+ external_agent_config["title"] = external_agent_config["name"]
310
+ external_agent_config["auth_config"] = {"token": credential}
311
+ external_agent_config["spec_version"] = external_agent_config.get("spec_version", "v1")
312
+ external_agent_config["provider"] = "external_chat"
313
+
314
+ with tempfile.NamedTemporaryFile(mode="w+", encoding="utf-8", suffix=".json", delete=True) as fp:
315
+ json.dump(external_agent_config, fp, indent=4)
316
+ fp.flush()
317
+ agents = agent_controller.import_agent(file=os.path.abspath(fp.name), app_id=None)
318
+ agent_controller.publish_or_update_agents(agents)
319
+
320
+ logger.info("Registering Native Agent")
321
+
322
+ native_agent_template = _native_agent_template()
323
+ agent_name = _random_native_agent_name(external_agent_config["name"])
324
+ rich.print(f"[blue][b]Generated native agent name is: [i]{agent_name}[/i][/b]")
325
+ native_agent_template["name"] = agent_name
326
+ native_agent_template["collaborators"] = [external_agent_config["name"]]
327
+
328
+ with tempfile.NamedTemporaryFile(mode="w+", encoding="utf-8", suffix=".json", delete=True) as fp:
329
+ json.dump(native_agent_template, fp, indent=4)
330
+ fp.flush()
331
+ agents = agent_controller.import_agent(file=os.path.abspath(fp.name), app_id=None)
332
+ agent_controller.publish_or_update_agents(agents)
264
333
 
265
334
  rich.print(f"[gold3]Starting evaluation of inputs in '{data_path}' against '{agent_name}'[/gold3]")
266
335
  performance_test(
@@ -271,8 +340,6 @@ def validate_external(
271
340
  )
272
341
 
273
342
  else:
274
- with open(external_agent_config, "r") as f:
275
- external_agent_config = yaml.safe_load(f)
276
343
  controller = EvaluationsController()
277
344
  test_data = []
278
345
  with open(data_path, "r") as f:
@@ -280,31 +347,29 @@ def validate_external(
280
347
  for line in csv_reader:
281
348
  test_data.append(line[0])
282
349
 
283
- # save validation results in "validation_results" sub-dir
284
- validation_folder = Path(output_dir) / "validation_results"
350
+ # save validation results in "validate_external" sub-dir
351
+ validation_folder = Path(output_dir) / "validate_external"
285
352
  if os.path.exists(validation_folder):
286
353
  rich.print(f"[yellow]: found existing {validation_folder} in target directory. All content is removed.")
287
354
  shutil.rmtree(validation_folder)
288
355
  validation_folder.mkdir(exist_ok=True, parents=True)
356
+ shutil.copy(data_path, os.path.join(validation_folder, "input_sample.tsv"))
289
357
 
290
358
  # validate the inputs in the provided csv file
291
359
  summary = controller.external_validate(external_agent_config, test_data, credential)
292
- with open(validation_folder / "validation_results.json", "w") as f:
293
- json.dump(summary, f, indent=4)
294
-
295
360
  # validate sample block inputs
296
- rich.print("[gold3]Validating external agent to see if it can handle an array of messages.")
361
+ rich.print("[gold3]Validating external agent against an array of messages.")
297
362
  block_input_summary = controller.external_validate(external_agent_config, test_data, credential, add_context=True)
298
- with open(validation_folder / "sample_block_validation_results.json", "w") as f:
299
- json.dump(block_input_summary, f, indent=4)
300
-
363
+
364
+ with open(validation_folder / "validation_results.json", "w") as f:
365
+ json.dump([summary, block_input_summary], f, indent=4)
366
+
301
367
  user_validation_successful = all([item["success"] for item in summary])
302
368
  block_validation_successful = all([item["success"] for item in block_input_summary])
303
369
 
304
370
  if user_validation_successful and block_validation_successful:
305
371
  msg = (
306
372
  f"[green]Validation is successful. The result is saved to '{str(validation_folder)}'.[/green]\n"
307
- "You can add the external agent as a collaborator agent. See: https://developer.watson-orchestrate.ibm.com/agents/build_agent#native-agents."
308
373
  )
309
374
  else:
310
375
  msg = f"[dark_orange]Schema validation did not succeed. See '{str(validation_folder)}' for failures.[/dark_orange]"
@@ -3,12 +3,12 @@ import os.path
3
3
  from typing import List, Dict, Optional, Tuple
4
4
  import csv
5
5
  from pathlib import Path
6
- import rich
6
+ import sys
7
7
  from wxo_agentic_evaluation import main as evaluate
8
8
  from wxo_agentic_evaluation.tool_planner import build_snapshot
9
9
  from wxo_agentic_evaluation.analyze_run import analyze
10
10
  from wxo_agentic_evaluation.batch_annotate import generate_test_cases_from_stories
11
- from wxo_agentic_evaluation.arg_configs import TestConfig, AuthConfig, LLMUserConfig, ChatRecordingConfig, AnalyzeConfig
11
+ from wxo_agentic_evaluation.arg_configs import TestConfig, AuthConfig, LLMUserConfig, ChatRecordingConfig, AnalyzeConfig, ProviderConfig
12
12
  from wxo_agentic_evaluation.record_chat import record_chats
13
13
  from wxo_agentic_evaluation.external_agent.external_validate import ExternalAgentValidation
14
14
  from wxo_agentic_evaluation.external_agent.performance_test import ExternalAgentPerformanceTest
@@ -41,12 +41,26 @@ class EvaluationsController:
41
41
  def evaluate(self, config_file: Optional[str] = None, test_paths: Optional[str] = None, output_dir: Optional[str] = None) -> None:
42
42
  url, tenant_name, token = self._get_env_config()
43
43
 
44
+ if "WATSONX_SPACE_ID" in os.environ and "WATSONX_APIKEY" in os.environ:
45
+ provider = "watsonx"
46
+ elif "WO_INSTANCE" in os.environ and "WO_API_KEY" in os.environ:
47
+ provider = "model_proxy"
48
+ else:
49
+ logger.error(
50
+ "No provider found. Please either provide a config_file or set either WATSONX_SPACE_ID and WATSONX_APIKEY or WO_INSTANCE and WO_API_KEY in your system environment variables."
51
+ )
52
+ sys.exit(1)
53
+
44
54
  config_data = {
45
55
  "wxo_lite_version": __version__,
46
56
  "auth_config": AuthConfig(
47
57
  url=url,
48
58
  tenant_name=tenant_name,
49
59
  token=token
60
+ ),
61
+ "provider_config": ProviderConfig(
62
+ provider=provider,
63
+ model_id="meta-llama/llama-3-405b-instruct",
50
64
  )
51
65
  }
52
66
 
@@ -62,6 +76,10 @@ class EvaluationsController:
62
76
  if "llm_user_config" in file_config:
63
77
  llm_config_data = file_config.pop("llm_user_config")
64
78
  config_data["llm_user_config"] = LLMUserConfig(**llm_config_data)
79
+
80
+ if "provider_config" in file_config:
81
+ provider_config_data = file_config.pop("provider_config")
82
+ config_data["provider_config"] = ProviderConfig(**provider_config_data)
65
83
 
66
84
  config_data.update(file_config)
67
85
 
@@ -21,24 +21,6 @@ def knowledge_base_import(
21
21
  controller = KnowledgeBaseController()
22
22
  controller.import_knowledge_base(file=file, app_id=app_id)
23
23
 
24
- @knowledge_bases_app.command(name="patch", help="Patch a knowledge base by uploading documents, or providing an external vector index")
25
- def knowledge_base_patch(
26
- file: Annotated[
27
- str,
28
- typer.Option("--file", "-f", help="YAML or JSON file with knowledge base definition"),
29
- ],
30
- name: Annotated[
31
- str,
32
- typer.Option("--name", "-n", help="Name of the knowledge base you wish to update"),
33
- ]=None,
34
- id: Annotated[
35
- str,
36
- typer.Option("--id", "-i", help="ID of the knowledge base you wish to update"),
37
- ]=None
38
- ):
39
- controller = KnowledgeBaseController()
40
- controller.update_knowledge_base(id=id, name=name, file=file)
41
-
42
24
 
43
25
  @knowledge_bases_app.command(name="list", help="List all knowledge bases")
44
26
  def list_knowledge_bases(
@@ -8,7 +8,6 @@ import inspect
8
8
  from pathlib import Path
9
9
  from typing import List
10
10
 
11
- from ibm_watsonx_orchestrate.agent_builder.knowledge_bases.knowledge_base_requests import KnowledgeBaseUpdateRequest
12
11
  from ibm_watsonx_orchestrate.agent_builder.knowledge_bases.knowledge_base import KnowledgeBase
13
12
  from ibm_watsonx_orchestrate.client.knowledge_bases.knowledge_base_client import KnowledgeBaseClient
14
13
  from ibm_watsonx_orchestrate.client.base_api_client import ClientAPIException
@@ -72,11 +71,21 @@ class KnowledgeBaseController:
72
71
  client = self.get_client()
73
72
 
74
73
  knowledge_bases = parse_file(file=file)
74
+
75
+ existing_knowledge_bases = client.get_by_names([kb.name for kb in knowledge_bases])
76
+
75
77
  for kb in knowledge_bases:
76
78
  try:
79
+ file_dir = "/".join(file.split("/")[:-1])
80
+
81
+ existing = list(filter(lambda ex: ex.get('name') == kb.name, existing_knowledge_bases))
82
+ if len(existing) > 0:
83
+ logger.info(f"Existing knowledge base '{kb.name}' found. Updating...")
84
+ self.update_knowledge_base(existing[0].get("id"), kb=kb, file_dir=file_dir)
85
+ continue
86
+
77
87
  kb.validate_documents_or_index_exists()
78
88
  if kb.documents:
79
- file_dir = "/".join(file.split("/")[:-1])
80
89
  files = [('files', (get_file_name(file_path), open(get_relative_file_path(file_path, file_dir), 'rb'))) for file_path in kb.documents]
81
90
 
82
91
  kb.prioritize_built_in_index = True
@@ -106,10 +115,7 @@ class KnowledgeBaseController:
106
115
 
107
116
  logger.info(f"Successfully imported knowledge base '{kb.name}'")
108
117
  except ClientAPIException as e:
109
- if "duplicate key value violates unique constraint" in e.response.text:
110
- logger.error(f"A knowledge base with the name '{kb.name}' already exists. Failed to import knowledge base")
111
- else:
112
- logger.error(f"Error importing knowledge base '{kb.name}\n' {e.response.text}")
118
+ logger.error(f"Error importing knowledge base '{kb.name}\n' {e.response.text}")
113
119
 
114
120
  def get_id(
115
121
  self, id: str, name: str
@@ -131,27 +137,37 @@ class KnowledgeBaseController:
131
137
 
132
138
 
133
139
  def update_knowledge_base(
134
- self, id: str, name: str, file: str
135
- ) -> None:
136
- knowledge_base_id = self.get_id(id, name)
137
- update_request = KnowledgeBaseUpdateRequest.from_spec(file=file)
140
+ self, knowledge_base_id: str, kb: KnowledgeBase, file_dir: str
141
+ ) -> None:
142
+ if kb.documents:
143
+ status = self.get_client().status(knowledge_base_id)
144
+ existing_docs = [doc.get("metadata", {}).get("original_file_name", "") for doc in status.get("documents", [])]
145
+
146
+ removed_docs = existing_docs[:]
147
+ for filepath in kb.documents:
148
+ filename = get_file_name(filepath)
149
+
150
+ if filename in existing_docs:
151
+ logger.warning(f'Document \"{filename}\" already exists in knowledge base. Updating...')
152
+ removed_docs.remove(filename)
153
+
154
+ for filename in removed_docs:
155
+ logger.warning(f'Document \"{filename}\" removed from knowledge base.')
138
156
 
139
- if update_request.documents:
140
- file_dir = "/".join(file.split("/")[:-1])
141
- files = [('files', (get_file_name(file_path), open(get_relative_file_path(file_path, file_dir), 'rb'))) for file_path in update_request.documents]
157
+
158
+ files = [('files', (get_file_name(file_path), open(get_relative_file_path(file_path, file_dir), 'rb'))) for file_path in kb.documents]
142
159
 
143
- update_request.prioritize_built_in_index = True
144
- payload = update_request.model_dump(exclude_none=True);
160
+ kb.prioritize_built_in_index = True
161
+ payload = kb.model_dump(exclude_none=True);
145
162
  payload.pop('documents');
146
163
 
147
164
  self.get_client().update_with_documents(knowledge_base_id, payload=payload, files=files)
148
165
  else:
149
- if update_request.conversational_search_tool and update_request.conversational_search_tool.index_config:
150
- update_request.prioritize_built_in_index = False
151
- self.get_client().update(knowledge_base_id, update_request.model_dump(exclude_none=True))
166
+ if kb.conversational_search_tool and kb.conversational_search_tool.index_config:
167
+ kb.prioritize_built_in_index = False
168
+ self.get_client().update(knowledge_base_id, kb.model_dump(exclude_none=True))
152
169
 
153
- logEnding = f"with ID '{id}'" if id else f"'{name}'"
154
- logger.info(f"Successfully updated knowledge base {logEnding}")
170
+ logger.info(f"Knowledge base '{kb.name}' updated successfully")
155
171
 
156
172
 
157
173
  def knowledge_base_status( self, id: str, name: str) -> None:
@@ -149,7 +149,7 @@ def models_policy_add(
149
149
  retry_attempts: Annotated[
150
150
  int,
151
151
  typer.Option('--retry-attempts', help='The number of attempts to retry'),
152
- ],
152
+ ] = None,
153
153
  strategy_on_code: Annotated[
154
154
  List[int],
155
155
  typer.Option('--strategy-on-code', help='The http status to consider invoking the strategy'),