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
@@ -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
|
-
|
55
|
+
required_sets = [
|
56
|
+
["WATSONX_SPACE_ID", "WATSONX_APIKEY"],
|
57
|
+
["WO_INSTANCE", "WO_API_KEY"]
|
58
|
+
]
|
29
59
|
|
30
|
-
|
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
|
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
|
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
|
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
|
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
|
-
|
238
|
-
|
274
|
+
perf_test: Annotated[
|
275
|
+
bool,
|
239
276
|
typer.Option(
|
240
|
-
"--
|
241
|
-
help="
|
242
|
-
"
|
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
|
-
] =
|
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
|
-
|
253
|
-
|
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(
|
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.
|
261
|
-
|
262
|
-
|
263
|
-
|
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 "
|
284
|
-
validation_folder = Path(output_dir) / "
|
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
|
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
|
-
|
299
|
-
|
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
|
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
|
-
|
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,
|
135
|
-
) -> None:
|
136
|
-
|
137
|
-
|
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
|
-
|
140
|
-
|
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
|
-
|
144
|
-
payload =
|
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
|
150
|
-
|
151
|
-
self.get_client().update(knowledge_base_id,
|
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
|
-
|
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'),
|