kiln-ai 0.19.0__py3-none-any.whl → 0.21.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.
Potentially problematic release.
This version of kiln-ai might be problematic. Click here for more details.
- kiln_ai/adapters/__init__.py +8 -2
- kiln_ai/adapters/adapter_registry.py +43 -208
- kiln_ai/adapters/chat/chat_formatter.py +8 -12
- kiln_ai/adapters/chat/test_chat_formatter.py +6 -2
- kiln_ai/adapters/chunkers/__init__.py +13 -0
- kiln_ai/adapters/chunkers/base_chunker.py +42 -0
- kiln_ai/adapters/chunkers/chunker_registry.py +16 -0
- kiln_ai/adapters/chunkers/fixed_window_chunker.py +39 -0
- kiln_ai/adapters/chunkers/helpers.py +23 -0
- kiln_ai/adapters/chunkers/test_base_chunker.py +63 -0
- kiln_ai/adapters/chunkers/test_chunker_registry.py +28 -0
- kiln_ai/adapters/chunkers/test_fixed_window_chunker.py +346 -0
- kiln_ai/adapters/chunkers/test_helpers.py +75 -0
- kiln_ai/adapters/data_gen/test_data_gen_task.py +9 -3
- kiln_ai/adapters/docker_model_runner_tools.py +119 -0
- kiln_ai/adapters/embedding/__init__.py +0 -0
- kiln_ai/adapters/embedding/base_embedding_adapter.py +44 -0
- kiln_ai/adapters/embedding/embedding_registry.py +32 -0
- kiln_ai/adapters/embedding/litellm_embedding_adapter.py +199 -0
- kiln_ai/adapters/embedding/test_base_embedding_adapter.py +283 -0
- kiln_ai/adapters/embedding/test_embedding_registry.py +166 -0
- kiln_ai/adapters/embedding/test_litellm_embedding_adapter.py +1149 -0
- kiln_ai/adapters/eval/base_eval.py +2 -2
- kiln_ai/adapters/eval/eval_runner.py +9 -3
- kiln_ai/adapters/eval/g_eval.py +2 -2
- kiln_ai/adapters/eval/test_base_eval.py +2 -4
- kiln_ai/adapters/eval/test_g_eval.py +4 -5
- kiln_ai/adapters/extractors/__init__.py +18 -0
- kiln_ai/adapters/extractors/base_extractor.py +72 -0
- kiln_ai/adapters/extractors/encoding.py +20 -0
- kiln_ai/adapters/extractors/extractor_registry.py +44 -0
- kiln_ai/adapters/extractors/extractor_runner.py +112 -0
- kiln_ai/adapters/extractors/litellm_extractor.py +386 -0
- kiln_ai/adapters/extractors/test_base_extractor.py +244 -0
- kiln_ai/adapters/extractors/test_encoding.py +54 -0
- kiln_ai/adapters/extractors/test_extractor_registry.py +181 -0
- kiln_ai/adapters/extractors/test_extractor_runner.py +181 -0
- kiln_ai/adapters/extractors/test_litellm_extractor.py +1192 -0
- kiln_ai/adapters/fine_tune/__init__.py +1 -1
- kiln_ai/adapters/fine_tune/openai_finetune.py +14 -4
- kiln_ai/adapters/fine_tune/test_dataset_formatter.py +2 -2
- kiln_ai/adapters/fine_tune/test_fireworks_tinetune.py +2 -6
- kiln_ai/adapters/fine_tune/test_openai_finetune.py +108 -111
- kiln_ai/adapters/fine_tune/test_together_finetune.py +2 -6
- kiln_ai/adapters/ml_embedding_model_list.py +192 -0
- kiln_ai/adapters/ml_model_list.py +761 -37
- kiln_ai/adapters/model_adapters/base_adapter.py +51 -21
- kiln_ai/adapters/model_adapters/litellm_adapter.py +380 -138
- kiln_ai/adapters/model_adapters/test_base_adapter.py +193 -17
- kiln_ai/adapters/model_adapters/test_litellm_adapter.py +407 -2
- kiln_ai/adapters/model_adapters/test_litellm_adapter_tools.py +1103 -0
- kiln_ai/adapters/model_adapters/test_saving_adapter_results.py +5 -5
- kiln_ai/adapters/model_adapters/test_structured_output.py +113 -5
- kiln_ai/adapters/ollama_tools.py +69 -12
- kiln_ai/adapters/parsers/__init__.py +1 -1
- kiln_ai/adapters/provider_tools.py +205 -47
- kiln_ai/adapters/rag/deduplication.py +49 -0
- kiln_ai/adapters/rag/progress.py +252 -0
- kiln_ai/adapters/rag/rag_runners.py +844 -0
- kiln_ai/adapters/rag/test_deduplication.py +195 -0
- kiln_ai/adapters/rag/test_progress.py +785 -0
- kiln_ai/adapters/rag/test_rag_runners.py +2376 -0
- kiln_ai/adapters/remote_config.py +80 -8
- kiln_ai/adapters/repair/test_repair_task.py +12 -9
- kiln_ai/adapters/run_output.py +3 -0
- kiln_ai/adapters/test_adapter_registry.py +657 -85
- kiln_ai/adapters/test_docker_model_runner_tools.py +305 -0
- kiln_ai/adapters/test_ml_embedding_model_list.py +429 -0
- kiln_ai/adapters/test_ml_model_list.py +251 -1
- kiln_ai/adapters/test_ollama_tools.py +340 -1
- kiln_ai/adapters/test_prompt_adaptors.py +13 -6
- kiln_ai/adapters/test_prompt_builders.py +1 -1
- kiln_ai/adapters/test_provider_tools.py +254 -8
- kiln_ai/adapters/test_remote_config.py +651 -58
- kiln_ai/adapters/vector_store/__init__.py +1 -0
- kiln_ai/adapters/vector_store/base_vector_store_adapter.py +83 -0
- kiln_ai/adapters/vector_store/lancedb_adapter.py +389 -0
- kiln_ai/adapters/vector_store/test_base_vector_store.py +160 -0
- kiln_ai/adapters/vector_store/test_lancedb_adapter.py +1841 -0
- kiln_ai/adapters/vector_store/test_vector_store_registry.py +199 -0
- kiln_ai/adapters/vector_store/vector_store_registry.py +33 -0
- kiln_ai/datamodel/__init__.py +39 -34
- kiln_ai/datamodel/basemodel.py +170 -1
- kiln_ai/datamodel/chunk.py +158 -0
- kiln_ai/datamodel/datamodel_enums.py +28 -0
- kiln_ai/datamodel/embedding.py +64 -0
- kiln_ai/datamodel/eval.py +1 -1
- kiln_ai/datamodel/external_tool_server.py +298 -0
- kiln_ai/datamodel/extraction.py +303 -0
- kiln_ai/datamodel/json_schema.py +25 -10
- kiln_ai/datamodel/project.py +40 -1
- kiln_ai/datamodel/rag.py +79 -0
- kiln_ai/datamodel/registry.py +0 -15
- kiln_ai/datamodel/run_config.py +62 -0
- kiln_ai/datamodel/task.py +2 -77
- kiln_ai/datamodel/task_output.py +6 -1
- kiln_ai/datamodel/task_run.py +41 -0
- kiln_ai/datamodel/test_attachment.py +649 -0
- kiln_ai/datamodel/test_basemodel.py +4 -4
- kiln_ai/datamodel/test_chunk_models.py +317 -0
- kiln_ai/datamodel/test_dataset_split.py +1 -1
- kiln_ai/datamodel/test_embedding_models.py +448 -0
- kiln_ai/datamodel/test_eval_model.py +6 -6
- kiln_ai/datamodel/test_example_models.py +175 -0
- kiln_ai/datamodel/test_external_tool_server.py +691 -0
- kiln_ai/datamodel/test_extraction_chunk.py +206 -0
- kiln_ai/datamodel/test_extraction_model.py +470 -0
- kiln_ai/datamodel/test_rag.py +641 -0
- kiln_ai/datamodel/test_registry.py +8 -3
- kiln_ai/datamodel/test_task.py +15 -47
- kiln_ai/datamodel/test_tool_id.py +320 -0
- kiln_ai/datamodel/test_vector_store.py +320 -0
- kiln_ai/datamodel/tool_id.py +105 -0
- kiln_ai/datamodel/vector_store.py +141 -0
- kiln_ai/tools/__init__.py +8 -0
- kiln_ai/tools/base_tool.py +82 -0
- kiln_ai/tools/built_in_tools/__init__.py +13 -0
- kiln_ai/tools/built_in_tools/math_tools.py +124 -0
- kiln_ai/tools/built_in_tools/test_math_tools.py +204 -0
- kiln_ai/tools/mcp_server_tool.py +95 -0
- kiln_ai/tools/mcp_session_manager.py +246 -0
- kiln_ai/tools/rag_tools.py +157 -0
- kiln_ai/tools/test_base_tools.py +199 -0
- kiln_ai/tools/test_mcp_server_tool.py +457 -0
- kiln_ai/tools/test_mcp_session_manager.py +1585 -0
- kiln_ai/tools/test_rag_tools.py +848 -0
- kiln_ai/tools/test_tool_registry.py +562 -0
- kiln_ai/tools/tool_registry.py +85 -0
- kiln_ai/utils/__init__.py +3 -0
- kiln_ai/utils/async_job_runner.py +62 -17
- kiln_ai/utils/config.py +24 -2
- kiln_ai/utils/env.py +15 -0
- kiln_ai/utils/filesystem.py +14 -0
- kiln_ai/utils/filesystem_cache.py +60 -0
- kiln_ai/utils/litellm.py +94 -0
- kiln_ai/utils/lock.py +100 -0
- kiln_ai/utils/mime_type.py +38 -0
- kiln_ai/utils/open_ai_types.py +94 -0
- kiln_ai/utils/pdf_utils.py +38 -0
- kiln_ai/utils/project_utils.py +17 -0
- kiln_ai/utils/test_async_job_runner.py +151 -35
- kiln_ai/utils/test_config.py +138 -1
- kiln_ai/utils/test_env.py +142 -0
- kiln_ai/utils/test_filesystem_cache.py +316 -0
- kiln_ai/utils/test_litellm.py +206 -0
- kiln_ai/utils/test_lock.py +185 -0
- kiln_ai/utils/test_mime_type.py +66 -0
- kiln_ai/utils/test_open_ai_types.py +131 -0
- kiln_ai/utils/test_pdf_utils.py +73 -0
- kiln_ai/utils/test_uuid.py +111 -0
- kiln_ai/utils/test_validation.py +524 -0
- kiln_ai/utils/uuid.py +9 -0
- kiln_ai/utils/validation.py +90 -0
- {kiln_ai-0.19.0.dist-info → kiln_ai-0.21.0.dist-info}/METADATA +12 -5
- kiln_ai-0.21.0.dist-info/RECORD +211 -0
- kiln_ai-0.19.0.dist-info/RECORD +0 -115
- {kiln_ai-0.19.0.dist-info → kiln_ai-0.21.0.dist-info}/WHEEL +0 -0
- {kiln_ai-0.19.0.dist-info → kiln_ai-0.21.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -6,7 +6,11 @@ from kiln_ai.adapters.ml_model_list import KilnModelProvider, StructuredOutputMo
|
|
|
6
6
|
from kiln_ai.adapters.model_adapters.base_adapter import BaseAdapter, RunOutput
|
|
7
7
|
from kiln_ai.datamodel import Task
|
|
8
8
|
from kiln_ai.datamodel.datamodel_enums import ChatStrategy
|
|
9
|
-
from kiln_ai.datamodel.
|
|
9
|
+
from kiln_ai.datamodel.project import Project
|
|
10
|
+
from kiln_ai.datamodel.run_config import ToolsRunConfig
|
|
11
|
+
from kiln_ai.datamodel.task import RunConfigProperties
|
|
12
|
+
from kiln_ai.datamodel.tool_id import KilnBuiltInToolId
|
|
13
|
+
from kiln_ai.tools.base_tool import KilnToolInterface
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
class MockAdapter(BaseAdapter):
|
|
@@ -27,15 +31,21 @@ def mock_provider():
|
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
@pytest.fixture
|
|
30
|
-
def
|
|
31
|
-
return
|
|
34
|
+
def base_project():
|
|
35
|
+
return Project(name="test_project", description="test project description")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def base_task(base_project):
|
|
40
|
+
task = Task(name="test_task", instruction="test_instruction", parent=base_project)
|
|
41
|
+
return task
|
|
32
42
|
|
|
33
43
|
|
|
34
44
|
@pytest.fixture
|
|
35
45
|
def adapter(base_task):
|
|
36
46
|
return MockAdapter(
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
task=base_task,
|
|
48
|
+
run_config=RunConfigProperties(
|
|
39
49
|
model_name="test_model",
|
|
40
50
|
model_provider_name="openai",
|
|
41
51
|
prompt_id="simple_prompt_builder",
|
|
@@ -98,13 +108,16 @@ async def test_model_provider_loads_and_caches(adapter, mock_provider):
|
|
|
98
108
|
mock_loader.assert_not_called()
|
|
99
109
|
|
|
100
110
|
|
|
101
|
-
async def test_model_provider_invalid_provider_model_name(
|
|
111
|
+
async def test_model_provider_invalid_provider_model_name(base_project):
|
|
102
112
|
"""Test error when model or provider name is missing"""
|
|
113
|
+
# Create a task with a parent project
|
|
114
|
+
task = Task(name="test_task", instruction="test_instruction", parent=base_project)
|
|
115
|
+
|
|
103
116
|
# Test with missing model name
|
|
104
117
|
with pytest.raises(ValueError, match="Input should be"):
|
|
105
118
|
MockAdapter(
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
task=task,
|
|
120
|
+
run_config=RunConfigProperties(
|
|
108
121
|
model_name="test_model",
|
|
109
122
|
model_provider_name="invalid",
|
|
110
123
|
prompt_id="simple_prompt_builder",
|
|
@@ -112,12 +125,15 @@ async def test_model_provider_invalid_provider_model_name(base_task):
|
|
|
112
125
|
)
|
|
113
126
|
|
|
114
127
|
|
|
115
|
-
async def test_model_provider_missing_model_names(
|
|
128
|
+
async def test_model_provider_missing_model_names(base_project):
|
|
116
129
|
"""Test error when model or provider name is missing"""
|
|
130
|
+
# Create a task with a parent project
|
|
131
|
+
task = Task(name="test_task", instruction="test_instruction", parent=base_project)
|
|
132
|
+
|
|
117
133
|
# Test with missing model name
|
|
118
134
|
adapter = MockAdapter(
|
|
119
|
-
|
|
120
|
-
|
|
135
|
+
task=task,
|
|
136
|
+
run_config=RunConfigProperties(
|
|
121
137
|
model_name="",
|
|
122
138
|
model_provider_name="openai",
|
|
123
139
|
prompt_id="simple_prompt_builder",
|
|
@@ -252,12 +268,13 @@ async def test_properties_for_task_output_includes_all_run_config_properties(ada
|
|
|
252
268
|
"temperature": "temperature",
|
|
253
269
|
"top_p": "top_p",
|
|
254
270
|
"structured_output_mode": "structured_output_mode",
|
|
271
|
+
"tools_config": None,
|
|
255
272
|
}
|
|
256
273
|
|
|
257
274
|
missing_properties = []
|
|
258
275
|
for field_name in run_config_properties_fields:
|
|
259
276
|
expected_key = expected_mappings.get(field_name, field_name)
|
|
260
|
-
if expected_key not in saved_property_keys:
|
|
277
|
+
if expected_key is not None and expected_key not in saved_property_keys:
|
|
261
278
|
missing_properties.append(
|
|
262
279
|
f"RunConfigProperties.{field_name} -> {expected_key}"
|
|
263
280
|
)
|
|
@@ -297,12 +314,13 @@ async def test_properties_for_task_output_catches_missing_new_property(adapter):
|
|
|
297
314
|
"temperature": "temperature",
|
|
298
315
|
"top_p": "top_p",
|
|
299
316
|
"structured_output_mode": "structured_output_mode",
|
|
317
|
+
"tools_config": None,
|
|
300
318
|
}
|
|
301
319
|
|
|
302
320
|
missing_properties = []
|
|
303
321
|
for field_name in run_config_properties_fields:
|
|
304
322
|
expected_key = expected_mappings.get(field_name, field_name)
|
|
305
|
-
if expected_key not in saved_property_keys:
|
|
323
|
+
if expected_key is not None and expected_key not in saved_property_keys:
|
|
306
324
|
missing_properties.append(
|
|
307
325
|
f"RunConfigProperties.{field_name} -> {expected_key}"
|
|
308
326
|
)
|
|
@@ -408,12 +426,14 @@ def test_build_chat_formatter(
|
|
|
408
426
|
],
|
|
409
427
|
)
|
|
410
428
|
async def test_update_run_config_unknown_structured_output_mode(
|
|
411
|
-
|
|
429
|
+
base_project, initial_mode, expected_mode
|
|
412
430
|
):
|
|
413
431
|
"""Test that unknown structured output mode is updated to the default for the model provider"""
|
|
432
|
+
# Create a task with a parent project
|
|
433
|
+
task = Task(name="test_task", instruction="test_instruction", parent=base_project)
|
|
434
|
+
|
|
414
435
|
# Create a run config with the initial mode
|
|
415
|
-
run_config =
|
|
416
|
-
task=base_task,
|
|
436
|
+
run_config = RunConfigProperties(
|
|
417
437
|
model_name="test_model",
|
|
418
438
|
model_provider_name="openai",
|
|
419
439
|
prompt_id="simple_prompt_builder",
|
|
@@ -429,7 +449,7 @@ async def test_update_run_config_unknown_structured_output_mode(
|
|
|
429
449
|
mock_default.return_value = StructuredOutputMode.json_mode
|
|
430
450
|
|
|
431
451
|
# Create the adapter
|
|
432
|
-
adapter = MockAdapter(run_config=run_config)
|
|
452
|
+
adapter = MockAdapter(task=task, run_config=run_config)
|
|
433
453
|
|
|
434
454
|
# Verify the mode was updated correctly
|
|
435
455
|
assert adapter.run_config.structured_output_mode == expected_mode
|
|
@@ -443,3 +463,159 @@ async def test_update_run_config_unknown_structured_output_mode(
|
|
|
443
463
|
mock_default.assert_called_once_with("test_model", "openai")
|
|
444
464
|
else:
|
|
445
465
|
mock_default.assert_not_called()
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
@pytest.mark.parametrize(
|
|
469
|
+
"tools_config,expected_tool_count,expected_tool_ids",
|
|
470
|
+
[
|
|
471
|
+
# No tools config
|
|
472
|
+
(None, 0, []),
|
|
473
|
+
# Empty tools config with None tools
|
|
474
|
+
(ToolsRunConfig(tools=[]), 0, []),
|
|
475
|
+
# Single tool
|
|
476
|
+
([KilnBuiltInToolId.ADD_NUMBERS], 1, [KilnBuiltInToolId.ADD_NUMBERS]),
|
|
477
|
+
# Multiple tools
|
|
478
|
+
(
|
|
479
|
+
[KilnBuiltInToolId.ADD_NUMBERS, KilnBuiltInToolId.SUBTRACT_NUMBERS],
|
|
480
|
+
2,
|
|
481
|
+
[KilnBuiltInToolId.ADD_NUMBERS, KilnBuiltInToolId.SUBTRACT_NUMBERS],
|
|
482
|
+
),
|
|
483
|
+
# All available built-in tools
|
|
484
|
+
(
|
|
485
|
+
[
|
|
486
|
+
KilnBuiltInToolId.ADD_NUMBERS,
|
|
487
|
+
KilnBuiltInToolId.SUBTRACT_NUMBERS,
|
|
488
|
+
KilnBuiltInToolId.MULTIPLY_NUMBERS,
|
|
489
|
+
KilnBuiltInToolId.DIVIDE_NUMBERS,
|
|
490
|
+
],
|
|
491
|
+
4,
|
|
492
|
+
[
|
|
493
|
+
KilnBuiltInToolId.ADD_NUMBERS,
|
|
494
|
+
KilnBuiltInToolId.SUBTRACT_NUMBERS,
|
|
495
|
+
KilnBuiltInToolId.MULTIPLY_NUMBERS,
|
|
496
|
+
KilnBuiltInToolId.DIVIDE_NUMBERS,
|
|
497
|
+
],
|
|
498
|
+
),
|
|
499
|
+
],
|
|
500
|
+
)
|
|
501
|
+
async def test_available_tools(
|
|
502
|
+
base_project, tools_config, expected_tool_count, expected_tool_ids
|
|
503
|
+
):
|
|
504
|
+
"""Test that available_tools returns correct tools based on tools_config"""
|
|
505
|
+
# Create a task with a parent project
|
|
506
|
+
task = Task(name="test_task", instruction="test_instruction", parent=base_project)
|
|
507
|
+
|
|
508
|
+
# Create tools config if we have tool IDs
|
|
509
|
+
if tools_config is None:
|
|
510
|
+
final_tools_config = None
|
|
511
|
+
elif isinstance(tools_config, list):
|
|
512
|
+
final_tools_config = ToolsRunConfig(tools=tools_config)
|
|
513
|
+
else:
|
|
514
|
+
final_tools_config = tools_config
|
|
515
|
+
|
|
516
|
+
# Create adapter with tools config
|
|
517
|
+
adapter = MockAdapter(
|
|
518
|
+
task=task,
|
|
519
|
+
run_config=RunConfigProperties(
|
|
520
|
+
model_name="test_model",
|
|
521
|
+
model_provider_name="openai",
|
|
522
|
+
prompt_id="simple_prompt_builder",
|
|
523
|
+
structured_output_mode="json_schema",
|
|
524
|
+
tools_config=final_tools_config,
|
|
525
|
+
),
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
# Get available tools
|
|
529
|
+
tools = await adapter.available_tools()
|
|
530
|
+
|
|
531
|
+
# Verify tool count
|
|
532
|
+
assert len(tools) == expected_tool_count
|
|
533
|
+
|
|
534
|
+
# Verify all tools implement KilnToolInterface
|
|
535
|
+
for tool in tools:
|
|
536
|
+
assert isinstance(tool, KilnToolInterface)
|
|
537
|
+
|
|
538
|
+
# Verify tool IDs match expected
|
|
539
|
+
if expected_tool_ids:
|
|
540
|
+
actual_tool_ids = [await tool.id() for tool in tools]
|
|
541
|
+
assert actual_tool_ids == expected_tool_ids
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
async def test_available_tools_with_invalid_tool_id(base_project):
|
|
545
|
+
"""Test that available_tools raises ValueError for invalid tool ID"""
|
|
546
|
+
# Create a task with a parent project
|
|
547
|
+
task = Task(name="test_task", instruction="test_instruction", parent=base_project)
|
|
548
|
+
|
|
549
|
+
# Create tools config with valid tool ID
|
|
550
|
+
tools_config = ToolsRunConfig(tools=[KilnBuiltInToolId.ADD_NUMBERS])
|
|
551
|
+
|
|
552
|
+
# Create adapter
|
|
553
|
+
adapter = MockAdapter(
|
|
554
|
+
task=task,
|
|
555
|
+
run_config=RunConfigProperties(
|
|
556
|
+
model_name="test_model",
|
|
557
|
+
model_provider_name="openai",
|
|
558
|
+
prompt_id="simple_prompt_builder",
|
|
559
|
+
structured_output_mode="json_schema",
|
|
560
|
+
tools_config=tools_config,
|
|
561
|
+
),
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
# Mock tool_from_id to raise ValueError for any tool ID
|
|
565
|
+
with patch(
|
|
566
|
+
"kiln_ai.adapters.model_adapters.base_adapter.tool_from_id"
|
|
567
|
+
) as mock_tool_from_id:
|
|
568
|
+
mock_tool_from_id.side_effect = ValueError(
|
|
569
|
+
"Tool ID test_id not found in tool registry"
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Should raise ValueError when trying to get tools
|
|
573
|
+
with pytest.raises(
|
|
574
|
+
ValueError, match="Tool ID test_id not found in tool registry"
|
|
575
|
+
):
|
|
576
|
+
await adapter.available_tools()
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
async def test_available_tools_duplicate_names_raises_error(base_project):
|
|
580
|
+
"""Test that available_tools raises ValueError when tools have duplicate names"""
|
|
581
|
+
# Create a task with a parent project
|
|
582
|
+
task = Task(name="test_task", instruction="test_instruction", parent=base_project)
|
|
583
|
+
|
|
584
|
+
# Create tools config with two different tool IDs
|
|
585
|
+
tools_config = ToolsRunConfig(
|
|
586
|
+
tools=[KilnBuiltInToolId.ADD_NUMBERS, KilnBuiltInToolId.SUBTRACT_NUMBERS]
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
# Create adapter
|
|
590
|
+
adapter = MockAdapter(
|
|
591
|
+
task=task,
|
|
592
|
+
run_config=RunConfigProperties(
|
|
593
|
+
model_name="test_model",
|
|
594
|
+
model_provider_name="openai",
|
|
595
|
+
prompt_id="simple_prompt_builder",
|
|
596
|
+
structured_output_mode="json_schema",
|
|
597
|
+
tools_config=tools_config,
|
|
598
|
+
),
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
# Create mock tools with duplicate names
|
|
602
|
+
async def mock_name1():
|
|
603
|
+
return "duplicate_name"
|
|
604
|
+
|
|
605
|
+
async def mock_name2():
|
|
606
|
+
return "duplicate_name"
|
|
607
|
+
|
|
608
|
+
mock_tool1 = MagicMock(spec=KilnToolInterface)
|
|
609
|
+
mock_tool1.name = mock_name1
|
|
610
|
+
mock_tool2 = MagicMock(spec=KilnToolInterface)
|
|
611
|
+
mock_tool2.name = mock_name2 # Same name as tool1
|
|
612
|
+
|
|
613
|
+
# Mock tool_from_id to return our mock tools with duplicate names
|
|
614
|
+
with patch(
|
|
615
|
+
"kiln_ai.adapters.model_adapters.base_adapter.tool_from_id"
|
|
616
|
+
) as mock_tool_from_id:
|
|
617
|
+
mock_tool_from_id.side_effect = [mock_tool1, mock_tool2]
|
|
618
|
+
|
|
619
|
+
# Should raise ValueError when tools have duplicate names
|
|
620
|
+
with pytest.raises(ValueError, match="Each tool must have a unique name"):
|
|
621
|
+
await adapter.available_tools()
|