kiln-ai 0.13.2__py3-none-any.whl → 0.15.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/eval/base_eval.py +7 -2
- kiln_ai/adapters/fine_tune/base_finetune.py +6 -23
- kiln_ai/adapters/fine_tune/dataset_formatter.py +4 -4
- kiln_ai/adapters/fine_tune/finetune_registry.py +2 -0
- kiln_ai/adapters/fine_tune/fireworks_finetune.py +163 -15
- kiln_ai/adapters/fine_tune/test_base_finetune.py +7 -9
- kiln_ai/adapters/fine_tune/test_dataset_formatter.py +3 -3
- kiln_ai/adapters/fine_tune/test_fireworks_tinetune.py +495 -9
- kiln_ai/adapters/fine_tune/test_vertex_finetune.py +586 -0
- kiln_ai/adapters/fine_tune/vertex_finetune.py +217 -0
- kiln_ai/adapters/ml_model_list.py +319 -43
- kiln_ai/adapters/model_adapters/base_adapter.py +15 -10
- kiln_ai/adapters/model_adapters/litellm_adapter.py +10 -5
- kiln_ai/adapters/provider_tools.py +7 -0
- kiln_ai/adapters/test_provider_tools.py +16 -0
- kiln_ai/datamodel/json_schema.py +24 -7
- kiln_ai/datamodel/task_output.py +9 -5
- kiln_ai/datamodel/task_run.py +29 -5
- kiln_ai/datamodel/test_example_models.py +104 -3
- kiln_ai/datamodel/test_json_schema.py +22 -3
- kiln_ai/datamodel/test_model_perf.py +3 -2
- {kiln_ai-0.13.2.dist-info → kiln_ai-0.15.0.dist-info}/METADATA +3 -2
- {kiln_ai-0.13.2.dist-info → kiln_ai-0.15.0.dist-info}/RECORD +25 -24
- kiln_ai/adapters/test_generate_docs.py +0 -69
- {kiln_ai-0.13.2.dist-info → kiln_ai-0.15.0.dist-info}/WHEEL +0 -0
- {kiln_ai-0.13.2.dist-info → kiln_ai-0.15.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -3,20 +3,16 @@ from abc import ABCMeta, abstractmethod
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Dict, Literal, Tuple
|
|
5
5
|
|
|
6
|
+
import jsonschema
|
|
7
|
+
|
|
6
8
|
from kiln_ai.adapters.ml_model_list import KilnModelProvider, StructuredOutputMode
|
|
7
9
|
from kiln_ai.adapters.parsers.json_parser import parse_json_string
|
|
8
10
|
from kiln_ai.adapters.parsers.parser_registry import model_parser_from_id
|
|
9
11
|
from kiln_ai.adapters.prompt_builders import prompt_builder_from_id
|
|
10
12
|
from kiln_ai.adapters.provider_tools import kiln_model_provider_from
|
|
11
13
|
from kiln_ai.adapters.run_output import RunOutput
|
|
12
|
-
from kiln_ai.datamodel import
|
|
13
|
-
|
|
14
|
-
DataSourceType,
|
|
15
|
-
Task,
|
|
16
|
-
TaskOutput,
|
|
17
|
-
TaskRun,
|
|
18
|
-
)
|
|
19
|
-
from kiln_ai.datamodel.json_schema import validate_schema
|
|
14
|
+
from kiln_ai.datamodel import DataSource, DataSourceType, Task, TaskOutput, TaskRun
|
|
15
|
+
from kiln_ai.datamodel.json_schema import validate_schema_with_value_error
|
|
20
16
|
from kiln_ai.datamodel.task import RunConfig
|
|
21
17
|
from kiln_ai.utils.config import Config
|
|
22
18
|
|
|
@@ -103,7 +99,12 @@ class BaseAdapter(metaclass=ABCMeta):
|
|
|
103
99
|
if self.input_schema is not None:
|
|
104
100
|
if not isinstance(input, dict):
|
|
105
101
|
raise ValueError(f"structured input is not a dict: {input}")
|
|
106
|
-
|
|
102
|
+
|
|
103
|
+
validate_schema_with_value_error(
|
|
104
|
+
input,
|
|
105
|
+
self.input_schema,
|
|
106
|
+
"This task requires a specific input schema. While the model produced JSON, that JSON didn't meet the schema. Search 'Troubleshooting Structured Data Issues' in our docs for more information.",
|
|
107
|
+
)
|
|
107
108
|
|
|
108
109
|
# Run
|
|
109
110
|
run_output = await self._run(input)
|
|
@@ -125,7 +126,11 @@ class BaseAdapter(metaclass=ABCMeta):
|
|
|
125
126
|
raise RuntimeError(
|
|
126
127
|
f"structured response is not a dict: {parsed_output.output}"
|
|
127
128
|
)
|
|
128
|
-
|
|
129
|
+
validate_schema_with_value_error(
|
|
130
|
+
parsed_output.output,
|
|
131
|
+
self.output_schema,
|
|
132
|
+
"This task requires a specific output schema. While the model produced JSON, that JSON didn't meet the schema. Search 'Troubleshooting Structured Data Issues' in our docs for more information.",
|
|
133
|
+
)
|
|
129
134
|
else:
|
|
130
135
|
if not isinstance(parsed_output.output, str):
|
|
131
136
|
raise RuntimeError(
|
|
@@ -65,6 +65,7 @@ class LiteLlmAdapter(BaseAdapter):
|
|
|
65
65
|
run_strategy, cot_prompt = self.run_strategy()
|
|
66
66
|
|
|
67
67
|
if run_strategy == "cot_as_message":
|
|
68
|
+
# Used for reasoning-capable models that can output thinking and structured format
|
|
68
69
|
if not cot_prompt:
|
|
69
70
|
raise ValueError("cot_prompt is required for cot_as_message strategy")
|
|
70
71
|
messages.append({"role": "system", "content": cot_prompt})
|
|
@@ -73,9 +74,11 @@ class LiteLlmAdapter(BaseAdapter):
|
|
|
73
74
|
raise ValueError("cot_prompt is required for cot_two_call strategy")
|
|
74
75
|
messages.append({"role": "system", "content": cot_prompt})
|
|
75
76
|
|
|
76
|
-
# First call for chain of thought
|
|
77
|
+
# First call for chain of thought
|
|
78
|
+
# No response format as this request is for "thinking" in plain text
|
|
79
|
+
# No logprobs as only needed for final answer
|
|
77
80
|
completion_kwargs = await self.build_completion_kwargs(
|
|
78
|
-
provider, messages, None
|
|
81
|
+
provider, messages, None, skip_response_format=True
|
|
79
82
|
)
|
|
80
83
|
cot_response = await litellm.acompletion(**completion_kwargs)
|
|
81
84
|
if (
|
|
@@ -367,6 +370,7 @@ class LiteLlmAdapter(BaseAdapter):
|
|
|
367
370
|
provider: KilnModelProvider,
|
|
368
371
|
messages: list[dict[str, Any]],
|
|
369
372
|
top_logprobs: int | None,
|
|
373
|
+
skip_response_format: bool = False,
|
|
370
374
|
) -> dict[str, Any]:
|
|
371
375
|
extra_body = self.build_extra_body(provider)
|
|
372
376
|
|
|
@@ -380,9 +384,10 @@ class LiteLlmAdapter(BaseAdapter):
|
|
|
380
384
|
**self._additional_body_options,
|
|
381
385
|
}
|
|
382
386
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
387
|
+
if not skip_response_format:
|
|
388
|
+
# Response format: json_schema, json_instructions, json_mode, function_calling, etc
|
|
389
|
+
response_format_options = await self.response_format_options()
|
|
390
|
+
completion_kwargs.update(response_format_options)
|
|
386
391
|
|
|
387
392
|
if top_logprobs is not None:
|
|
388
393
|
completion_kwargs["logprobs"] = True
|
|
@@ -268,6 +268,13 @@ def finetune_provider_model(
|
|
|
268
268
|
model_id=fine_tune.fine_tune_model_id,
|
|
269
269
|
)
|
|
270
270
|
|
|
271
|
+
if provider == ModelProviderName.vertex and fine_tune.fine_tune_model_id:
|
|
272
|
+
# Vertex AI trick: use the model_id "openai/endpoint_id". OpenAI calls the openai compatible API, which supports endpoint.
|
|
273
|
+
# Context: vertex has at least 3 APIS: vertex, openai compatible, and gemini. LiteLLM tries to infer which to use. This works
|
|
274
|
+
# on current LiteLLM version. Could also set base_model to gemini to tell it which to use, but same result.
|
|
275
|
+
endpoint_id = fine_tune.fine_tune_model_id.split("/")[-1]
|
|
276
|
+
model_provider.model_id = f"openai/{endpoint_id}"
|
|
277
|
+
|
|
271
278
|
if fine_tune.structured_output_mode is not None:
|
|
272
279
|
# If we know the model was trained with specific output mode, set it
|
|
273
280
|
model_provider.structured_output_mode = fine_tune.structured_output_mode
|
|
@@ -791,3 +791,19 @@ def test_finetune_from_id_cache_hit(mock_project, mock_task, mock_finetune):
|
|
|
791
791
|
mock_project.assert_not_called()
|
|
792
792
|
mock_task.assert_not_called()
|
|
793
793
|
mock_finetune.assert_not_called()
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def test_finetune_provider_model_vertex_ai(mock_project, mock_task, mock_finetune):
|
|
797
|
+
"""Test creation of provider for Vertex AI with endpoint ID transformation"""
|
|
798
|
+
finetune = Mock(spec=Finetune)
|
|
799
|
+
finetune.provider = ModelProviderName.vertex
|
|
800
|
+
finetune.fine_tune_model_id = "projects/123/locations/us-central1/endpoints/456"
|
|
801
|
+
finetune.structured_output_mode = StructuredOutputMode.json_mode
|
|
802
|
+
mock_finetune.return_value = finetune
|
|
803
|
+
|
|
804
|
+
provider = finetune_provider_model("project-123::task-456::finetune-789")
|
|
805
|
+
|
|
806
|
+
assert provider.name == ModelProviderName.vertex
|
|
807
|
+
# Verify the model_id is transformed into openai/endpoint_id format
|
|
808
|
+
assert provider.model_id == "openai/456"
|
|
809
|
+
assert provider.structured_output_mode == StructuredOutputMode.json_mode
|
kiln_ai/datamodel/json_schema.py
CHANGED
|
@@ -41,16 +41,33 @@ def validate_schema(instance: Dict, schema_str: str) -> None:
|
|
|
41
41
|
|
|
42
42
|
Raises:
|
|
43
43
|
jsonschema.exceptions.ValidationError: If validation fails
|
|
44
|
-
|
|
44
|
+
"""
|
|
45
|
+
schema = schema_from_json_str(schema_str)
|
|
46
|
+
v = jsonschema.Draft202012Validator(schema)
|
|
47
|
+
v.validate(instance)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def validate_schema_with_value_error(
|
|
51
|
+
instance: Dict, schema_str: str, error_prefix: str | None = None
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Validate a dictionary against a JSON schema and raise a ValueError if the schema is invalid.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
instance: Dictionary to validate
|
|
57
|
+
schema_str: JSON schema string to validate against
|
|
58
|
+
error_prefix: Error message prefix to include in the ValueError
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ValueError: If the instance does not match the schema
|
|
45
62
|
"""
|
|
46
63
|
try:
|
|
47
|
-
|
|
48
|
-
v = jsonschema.Draft202012Validator(schema)
|
|
49
|
-
v.validate(instance)
|
|
64
|
+
validate_schema(instance, schema_str)
|
|
50
65
|
except jsonschema.exceptions.ValidationError as e:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
msg = f"The error from the schema check was: {e.message}. The JSON was: \n```json\n{instance}\n```"
|
|
67
|
+
if error_prefix:
|
|
68
|
+
msg = f"{error_prefix} {msg}"
|
|
69
|
+
|
|
70
|
+
raise ValueError(msg) from e
|
|
54
71
|
|
|
55
72
|
|
|
56
73
|
def schema_from_json_str(v: str) -> Dict:
|
kiln_ai/datamodel/task_output.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing_extensions import Self
|
|
|
9
9
|
|
|
10
10
|
from kiln_ai.datamodel.basemodel import ID_TYPE, KilnBaseModel
|
|
11
11
|
from kiln_ai.datamodel.datamodel_enums import TaskOutputRatingType
|
|
12
|
-
from kiln_ai.datamodel.json_schema import
|
|
12
|
+
from kiln_ai.datamodel.json_schema import validate_schema_with_value_error
|
|
13
13
|
from kiln_ai.datamodel.strict_mode import strict_mode
|
|
14
14
|
from kiln_ai.utils.exhaustive_error import raise_exhaustive_enum_error
|
|
15
15
|
|
|
@@ -308,11 +308,15 @@ class TaskOutput(KilnBaseModel):
|
|
|
308
308
|
# validate output
|
|
309
309
|
if task.output_json_schema is not None:
|
|
310
310
|
try:
|
|
311
|
-
|
|
312
|
-
except json.JSONDecodeError:
|
|
311
|
+
output_parsed = json.loads(self.output)
|
|
312
|
+
except json.JSONDecodeError as e:
|
|
313
313
|
raise ValueError("Output is not a valid JSON object")
|
|
314
|
-
|
|
315
|
-
|
|
314
|
+
|
|
315
|
+
validate_schema_with_value_error(
|
|
316
|
+
output_parsed,
|
|
317
|
+
task.output_json_schema,
|
|
318
|
+
"This task requires a specific output schema. While the model produced JSON, that JSON didn't meet the schema. Search 'Troubleshooting Structured Data Issues' in our docs for more information.",
|
|
319
|
+
)
|
|
316
320
|
return self
|
|
317
321
|
|
|
318
322
|
@model_validator(mode="after")
|
kiln_ai/datamodel/task_run.py
CHANGED
|
@@ -7,7 +7,7 @@ from pydantic import Field, ValidationInfo, model_validator
|
|
|
7
7
|
from typing_extensions import Self
|
|
8
8
|
|
|
9
9
|
from kiln_ai.datamodel.basemodel import KilnParentedModel
|
|
10
|
-
from kiln_ai.datamodel.json_schema import
|
|
10
|
+
from kiln_ai.datamodel.json_schema import validate_schema_with_value_error
|
|
11
11
|
from kiln_ai.datamodel.strict_mode import strict_mode
|
|
12
12
|
from kiln_ai.datamodel.task_output import DataSource, TaskOutput
|
|
13
13
|
|
|
@@ -87,14 +87,19 @@ class TaskRun(KilnParentedModel):
|
|
|
87
87
|
# don't validate this relationship until we have a path or parent. Give them time to build it (but will catch it before saving)
|
|
88
88
|
return self
|
|
89
89
|
|
|
90
|
-
# validate
|
|
90
|
+
# validate input
|
|
91
91
|
if task.input_json_schema is not None:
|
|
92
92
|
try:
|
|
93
|
-
|
|
93
|
+
input_parsed = json.loads(self.input)
|
|
94
94
|
except json.JSONDecodeError:
|
|
95
95
|
raise ValueError("Input is not a valid JSON object")
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
|
|
97
|
+
validate_schema_with_value_error(
|
|
98
|
+
input_parsed,
|
|
99
|
+
task.input_json_schema,
|
|
100
|
+
"Input does not match task input schema.",
|
|
101
|
+
)
|
|
102
|
+
|
|
98
103
|
self._last_validated_input = self.input
|
|
99
104
|
return self
|
|
100
105
|
|
|
@@ -131,6 +136,24 @@ class TaskRun(KilnParentedModel):
|
|
|
131
136
|
raise ValueError(
|
|
132
137
|
"Repaired output rating must be None. Repaired outputs are assumed to have a perfect rating, as they have been fixed."
|
|
133
138
|
)
|
|
139
|
+
|
|
140
|
+
task = self.parent_task()
|
|
141
|
+
if (
|
|
142
|
+
task is not None
|
|
143
|
+
and self.repaired_output.output is not None
|
|
144
|
+
and task.output_json_schema is not None
|
|
145
|
+
):
|
|
146
|
+
try:
|
|
147
|
+
output_parsed = json.loads(self.repaired_output.output)
|
|
148
|
+
except json.JSONDecodeError:
|
|
149
|
+
raise ValueError("Repaired output is not a valid JSON object")
|
|
150
|
+
|
|
151
|
+
validate_schema_with_value_error(
|
|
152
|
+
output_parsed,
|
|
153
|
+
task.output_json_schema,
|
|
154
|
+
"Repaired output does not match task output schema.",
|
|
155
|
+
)
|
|
156
|
+
|
|
134
157
|
if self.repair_instructions is None and self.repaired_output is not None:
|
|
135
158
|
raise ValueError(
|
|
136
159
|
"Repair instructions are required if providing a repaired output."
|
|
@@ -139,6 +162,7 @@ class TaskRun(KilnParentedModel):
|
|
|
139
162
|
raise ValueError(
|
|
140
163
|
"A repaired output is required if providing repair instructions."
|
|
141
164
|
)
|
|
165
|
+
|
|
142
166
|
return self
|
|
143
167
|
|
|
144
168
|
@model_validator(mode="after")
|
|
@@ -358,6 +358,9 @@ def test_task_output_schema_validation(tmp_path):
|
|
|
358
358
|
task_output.save_to_file()
|
|
359
359
|
|
|
360
360
|
|
|
361
|
+
_input_schema_match = "Input does not match task input schema"
|
|
362
|
+
|
|
363
|
+
|
|
361
364
|
def test_task_input_schema_validation(tmp_path):
|
|
362
365
|
# Create a project and task hierarchy
|
|
363
366
|
project = Project(name="Test Project", path=(tmp_path / "test_project"))
|
|
@@ -395,18 +398,18 @@ def test_task_input_schema_validation(tmp_path):
|
|
|
395
398
|
valid_task_output.save_to_file()
|
|
396
399
|
|
|
397
400
|
# Changing to invalid input
|
|
398
|
-
with pytest.raises(ValueError, match=
|
|
401
|
+
with pytest.raises(ValueError, match=_input_schema_match):
|
|
399
402
|
valid_task_output.input = '{"name": "John Doe", "age": "thirty"}'
|
|
400
403
|
valid_task_output.save_to_file()
|
|
401
404
|
|
|
402
405
|
# loading from file, then changing to invalid input
|
|
403
406
|
loaded_task_output = TaskRun.load_from_file(valid_task_output.path)
|
|
404
|
-
with pytest.raises(ValueError, match=
|
|
407
|
+
with pytest.raises(ValueError, match=_input_schema_match):
|
|
405
408
|
loaded_task_output.input = '{"name": "John Doe", "age": "thirty"}'
|
|
406
409
|
loaded_task_output.save_to_file()
|
|
407
410
|
|
|
408
411
|
# Invalid case: input does not match task input schema
|
|
409
|
-
with pytest.raises(ValueError, match=
|
|
412
|
+
with pytest.raises(ValueError, match=_input_schema_match):
|
|
410
413
|
task_output = TaskRun(
|
|
411
414
|
input='{"name": "John Doe", "age": "thirty"}',
|
|
412
415
|
input_source=DataSource(
|
|
@@ -642,3 +645,101 @@ def test_task_run_validate_repaired_output():
|
|
|
642
645
|
)
|
|
643
646
|
|
|
644
647
|
assert "Repaired output rating must be None" in str(exc_info.value)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def test_task_run_validate_repaired_output_structured(tmp_path):
|
|
651
|
+
# Create a project, task, and example hierarchy
|
|
652
|
+
project = Project(name="Test Project", path=(tmp_path / "test_project"))
|
|
653
|
+
project.save_to_file()
|
|
654
|
+
task = Task(
|
|
655
|
+
name="Test Task",
|
|
656
|
+
instruction="test instruction",
|
|
657
|
+
parent=project,
|
|
658
|
+
output_json_schema=json.dumps(
|
|
659
|
+
{
|
|
660
|
+
"type": "object",
|
|
661
|
+
"properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
|
|
662
|
+
"required": ["name", "age"],
|
|
663
|
+
}
|
|
664
|
+
),
|
|
665
|
+
)
|
|
666
|
+
task.save_to_file()
|
|
667
|
+
|
|
668
|
+
# test valid repaired output schema
|
|
669
|
+
task_run = TaskRun(
|
|
670
|
+
parent=task,
|
|
671
|
+
input="test input",
|
|
672
|
+
input_source=DataSource(
|
|
673
|
+
type=DataSourceType.human,
|
|
674
|
+
properties={"created_by": "john_doe"},
|
|
675
|
+
),
|
|
676
|
+
output=TaskOutput(
|
|
677
|
+
output='{"name": "John Doe", "age": 30}',
|
|
678
|
+
source=DataSource(
|
|
679
|
+
type=DataSourceType.human,
|
|
680
|
+
properties={"created_by": "john_doe"},
|
|
681
|
+
),
|
|
682
|
+
),
|
|
683
|
+
repair_instructions="Fix the output",
|
|
684
|
+
repaired_output=TaskOutput(
|
|
685
|
+
output='{"name": "John Doe", "age": 30}',
|
|
686
|
+
source=DataSource(
|
|
687
|
+
type=DataSourceType.human, properties={"created_by": "john_doe"}
|
|
688
|
+
),
|
|
689
|
+
),
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
assert task_run.repaired_output is not None
|
|
693
|
+
assert task_run.repaired_output.rating is None
|
|
694
|
+
|
|
695
|
+
# test invalid JSON
|
|
696
|
+
with pytest.raises(ValueError):
|
|
697
|
+
TaskRun(
|
|
698
|
+
parent=task,
|
|
699
|
+
input="test input",
|
|
700
|
+
input_source=DataSource(
|
|
701
|
+
type=DataSourceType.human,
|
|
702
|
+
properties={"created_by": "john_doe"},
|
|
703
|
+
),
|
|
704
|
+
output=TaskOutput(
|
|
705
|
+
output='{"name": "John Doe", "age": 30}',
|
|
706
|
+
source=DataSource(
|
|
707
|
+
type=DataSourceType.human,
|
|
708
|
+
properties={"created_by": "john_doe"},
|
|
709
|
+
),
|
|
710
|
+
),
|
|
711
|
+
repair_instructions="Fix the output",
|
|
712
|
+
repaired_output=TaskOutput(
|
|
713
|
+
output='{"name": "John Doe", "age": 30', # missing closing brace
|
|
714
|
+
source=DataSource(
|
|
715
|
+
type=DataSourceType.human,
|
|
716
|
+
properties={"created_by": "john_doe"},
|
|
717
|
+
),
|
|
718
|
+
),
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# test invalid repaired output schema
|
|
722
|
+
with pytest.raises(ValueError):
|
|
723
|
+
TaskRun(
|
|
724
|
+
parent=task,
|
|
725
|
+
input="test input",
|
|
726
|
+
input_source=DataSource(
|
|
727
|
+
type=DataSourceType.human,
|
|
728
|
+
properties={"created_by": "john_doe"},
|
|
729
|
+
),
|
|
730
|
+
output=TaskOutput(
|
|
731
|
+
output='{"name": "John Doe", "age": 30}',
|
|
732
|
+
source=DataSource(
|
|
733
|
+
type=DataSourceType.human,
|
|
734
|
+
properties={"created_by": "john_doe"},
|
|
735
|
+
),
|
|
736
|
+
),
|
|
737
|
+
repair_instructions="Fix the output",
|
|
738
|
+
repaired_output=TaskOutput(
|
|
739
|
+
output='{"name": "John Doe", "age": "thirty"}', # invalid schema
|
|
740
|
+
source=DataSource(
|
|
741
|
+
type=DataSourceType.human,
|
|
742
|
+
properties={"created_by": "john_doe"},
|
|
743
|
+
),
|
|
744
|
+
),
|
|
745
|
+
)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import jsonschema
|
|
1
2
|
import pytest
|
|
2
3
|
from pydantic import BaseModel
|
|
3
4
|
|
|
@@ -6,6 +7,7 @@ from kiln_ai.datamodel.json_schema import (
|
|
|
6
7
|
schema_from_json_str,
|
|
7
8
|
string_to_json_key,
|
|
8
9
|
validate_schema,
|
|
10
|
+
validate_schema_with_value_error,
|
|
9
11
|
)
|
|
10
12
|
|
|
11
13
|
|
|
@@ -71,15 +73,32 @@ def test_validate_schema_content():
|
|
|
71
73
|
o = {"setup": "asdf", "punchline": "asdf", "rating": 1}
|
|
72
74
|
validate_schema(o, json_joke_schema)
|
|
73
75
|
o = {"setup": "asdf"}
|
|
74
|
-
with pytest.raises(
|
|
76
|
+
with pytest.raises(jsonschema.exceptions.ValidationError):
|
|
75
77
|
validate_schema(0, json_joke_schema)
|
|
76
78
|
o = {"setup": "asdf", "punchline": "asdf"}
|
|
77
79
|
validate_schema(o, json_joke_schema)
|
|
78
80
|
o = {"setup": "asdf", "punchline": "asdf", "rating": "1"}
|
|
79
|
-
with pytest.raises(
|
|
81
|
+
with pytest.raises(jsonschema.exceptions.ValidationError):
|
|
80
82
|
validate_schema(o, json_joke_schema)
|
|
81
83
|
|
|
82
84
|
|
|
85
|
+
def test_validate_schema_content_with_value_error():
|
|
86
|
+
o = {"setup": "asdf", "punchline": "asdf", "rating": 1}
|
|
87
|
+
validate_schema_with_value_error(o, json_joke_schema, "PREFIX")
|
|
88
|
+
o = {"setup": "asdf"}
|
|
89
|
+
with pytest.raises(
|
|
90
|
+
ValueError, match="PREFIX The error from the schema check was: "
|
|
91
|
+
):
|
|
92
|
+
validate_schema_with_value_error(0, json_joke_schema, "PREFIX")
|
|
93
|
+
o = {"setup": "asdf", "punchline": "asdf"}
|
|
94
|
+
validate_schema_with_value_error(o, json_joke_schema, "PREFIX")
|
|
95
|
+
o = {"setup": "asdf", "punchline": "asdf", "rating": "1"}
|
|
96
|
+
with pytest.raises(
|
|
97
|
+
ValueError, match="PREFIX The error from the schema check was: "
|
|
98
|
+
):
|
|
99
|
+
validate_schema_with_value_error(o, json_joke_schema, "PREFIX")
|
|
100
|
+
|
|
101
|
+
|
|
83
102
|
json_triangle_schema = """{
|
|
84
103
|
"type": "object",
|
|
85
104
|
"properties": {
|
|
@@ -122,7 +141,7 @@ def test_triangle_schema():
|
|
|
122
141
|
assert schema["properties"]["c"]["type"] == "integer"
|
|
123
142
|
assert schema["required"] == ["a", "b", "c"]
|
|
124
143
|
validate_schema({"a": 1, "b": 2, "c": 3}, json_triangle_schema)
|
|
125
|
-
with pytest.raises(
|
|
144
|
+
with pytest.raises(jsonschema.exceptions.ValidationError):
|
|
126
145
|
validate_schema({"a": 1, "b": 2, "c": "3"}, json_triangle_schema)
|
|
127
146
|
|
|
128
147
|
|
|
@@ -119,7 +119,8 @@ def test_benchmark_load_from_file(benchmark, task_run):
|
|
|
119
119
|
avg_time_per_iteration = total_time / iterations
|
|
120
120
|
ops_per_second = 1.0 / avg_time_per_iteration
|
|
121
121
|
|
|
122
|
-
# I get 8k ops per second on my MBP. Lower value here for CI.
|
|
122
|
+
# I get 8k ops per second on my MBP. Lower value here for CI and parallel testing.
|
|
123
123
|
# Prior to optimization was 290 ops per second.
|
|
124
|
-
|
|
124
|
+
print(f"Ops per second: {ops_per_second:.6f}")
|
|
125
|
+
if ops_per_second < 500:
|
|
125
126
|
pytest.fail(f"Ops per second: {ops_per_second:.6f}, expected more than 1k ops")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kiln-ai
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: Kiln AI
|
|
5
5
|
Project-URL: Homepage, https://getkiln.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/Kiln-AI/kiln
|
|
@@ -19,7 +19,7 @@ Requires-Dist: boto3>=1.37.10
|
|
|
19
19
|
Requires-Dist: coverage>=7.6.4
|
|
20
20
|
Requires-Dist: google-cloud-aiplatform>=1.84.0
|
|
21
21
|
Requires-Dist: jsonschema>=4.23.0
|
|
22
|
-
Requires-Dist: litellm>=1.
|
|
22
|
+
Requires-Dist: litellm>=1.67.0
|
|
23
23
|
Requires-Dist: openai>=1.53.0
|
|
24
24
|
Requires-Dist: pdoc>=15.0.0
|
|
25
25
|
Requires-Dist: pydantic>=2.9.2
|
|
@@ -28,6 +28,7 @@ Requires-Dist: pytest-cov>=6.0.0
|
|
|
28
28
|
Requires-Dist: pyyaml>=6.0.2
|
|
29
29
|
Requires-Dist: together
|
|
30
30
|
Requires-Dist: typing-extensions>=4.12.2
|
|
31
|
+
Requires-Dist: vertexai>=1.43.0
|
|
31
32
|
Description-Content-Type: text/markdown
|
|
32
33
|
|
|
33
34
|
# Kiln AI Core Library
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
kiln_ai/__init__.py,sha256=Sc4z8LRVFMwJUoc_DPVUriSXTZ6PO9MaJ80PhRbKyB8,34
|
|
2
2
|
kiln_ai/adapters/__init__.py,sha256=XjGmWagEyOEVwVIAxjN5rYNsQWIEACT5DB7MMTxdPss,1005
|
|
3
3
|
kiln_ai/adapters/adapter_registry.py,sha256=KmMHYQ3mxpjVLE6D-hMNWCGt6Cw9JvnFn6nMb48GE8Y,9166
|
|
4
|
-
kiln_ai/adapters/ml_model_list.py,sha256=
|
|
4
|
+
kiln_ai/adapters/ml_model_list.py,sha256=RyRvPStx2TNGDjmRKSE02bOZjSWSWuJ030Ythu4Fgh4,68593
|
|
5
5
|
kiln_ai/adapters/ollama_tools.py,sha256=uObtLWfqKb9RXHN-TGGw2Y1FQlEMe0u8FgszI0zQn6U,3550
|
|
6
6
|
kiln_ai/adapters/prompt_builders.py,sha256=LYHTIaisQMBFtWDRIGo1QJgOsmQ-NBpQ8fI4eImHxaQ,15269
|
|
7
|
-
kiln_ai/adapters/provider_tools.py,sha256=
|
|
7
|
+
kiln_ai/adapters/provider_tools.py,sha256=ciFQfGJuTuHDj3FARY-sUqbSb-7oAT9lMGJGCBJoF4I,15309
|
|
8
8
|
kiln_ai/adapters/run_output.py,sha256=RAi2Qp6dmqJVNm3CxbNTdAuhitHfH5NiUGbf6ygUP-k,257
|
|
9
9
|
kiln_ai/adapters/test_adapter_registry.py,sha256=eDLHqv9mwgdde221pa47bTV87vCXwkUyjqsas-iFUrY,6123
|
|
10
|
-
kiln_ai/adapters/test_generate_docs.py,sha256=M-uKcgF3hQmlEFOJ0o7DyL-9RgitGzkfROV-Dxtooec,2770
|
|
11
10
|
kiln_ai/adapters/test_ollama_tools.py,sha256=xAUzL0IVmmXadVehJu1WjqbhpKEYGAgGt3pWx7hrubc,2514
|
|
12
11
|
kiln_ai/adapters/test_prompt_adaptors.py,sha256=J1ZGZ8GG7SxP3_J3Zw0e6XmZY4NyPmUGX3IPgjh2LD8,7767
|
|
13
12
|
kiln_ai/adapters/test_prompt_builders.py,sha256=5Xvfr-oQg_LLrle6UqfpRHWcPUYa8ywG3aL1rM7q1Jw,22054
|
|
14
|
-
kiln_ai/adapters/test_provider_tools.py,sha256=
|
|
13
|
+
kiln_ai/adapters/test_provider_tools.py,sha256=7s-njUBm_TJCTeNOh4TrP7R-Q6TXILAxbv-GK0p3YPU,27446
|
|
15
14
|
kiln_ai/adapters/data_gen/__init__.py,sha256=QTZWaf7kq5BorhPvexJfwDEKmjRmIbhwW9ei8LW2SIs,276
|
|
16
15
|
kiln_ai/adapters/data_gen/data_gen_prompts.py,sha256=kudjHnAz7L3q0k_NLyTlaIV7M0uRFrxXNcfcnjOE2uc,5810
|
|
17
16
|
kiln_ai/adapters/data_gen/data_gen_task.py,sha256=0PuYCcj09BtpgNj23mKj_L45mKZBdV5VreUeZ-Tj_xM,6642
|
|
18
17
|
kiln_ai/adapters/data_gen/test_data_gen_task.py,sha256=cRKUKMvC0uVompbmPTKwbnQ_N3c0cQDm4J_9H4Y5U18,10129
|
|
19
18
|
kiln_ai/adapters/eval/__init__.py,sha256=0ptbK0ZxWuraxGn_WMgmE1tcaq0k5t-g-52kVohvWCg,693
|
|
20
|
-
kiln_ai/adapters/eval/base_eval.py,sha256=
|
|
19
|
+
kiln_ai/adapters/eval/base_eval.py,sha256=IF4kYGt93bqJqSfj8UUaTng38fwPmi3cFKRSKUZhXJs,7381
|
|
21
20
|
kiln_ai/adapters/eval/eval_runner.py,sha256=h3DvRFM5J5LDJqaLzNJ-q9i5LRycv2J9Ev5nw1mUDUQ,10806
|
|
22
21
|
kiln_ai/adapters/eval/g_eval.py,sha256=d3UcBsZWeDt7cWp4uvDcfG7qdGLsGaZEBsIEqkpiWh4,15253
|
|
23
22
|
kiln_ai/adapters/eval/registry.py,sha256=gZ_s0VgEx79Fswkgi1tS4yOl7lzpkvUBJZ62RldhM_w,626
|
|
@@ -26,20 +25,22 @@ kiln_ai/adapters/eval/test_eval_runner.py,sha256=82WPE_frNRTSQ2lylqT0inkqcDgM72n
|
|
|
26
25
|
kiln_ai/adapters/eval/test_g_eval.py,sha256=-Stx7E0D-WAH1HWrRSp48CiGsf-no1SHeFF9IqVXeMI,16433
|
|
27
26
|
kiln_ai/adapters/eval/test_g_eval_data.py,sha256=8caiZfLWnXVX8alrBPrH7L7gqqSS9vO7u6PzcHurQcA,27769
|
|
28
27
|
kiln_ai/adapters/fine_tune/__init__.py,sha256=DxdTR60chwgck1aEoVYWyfWi6Ed2ZkdJj0lar-SEAj4,257
|
|
29
|
-
kiln_ai/adapters/fine_tune/base_finetune.py,sha256=
|
|
30
|
-
kiln_ai/adapters/fine_tune/dataset_formatter.py,sha256=
|
|
31
|
-
kiln_ai/adapters/fine_tune/finetune_registry.py,sha256=
|
|
32
|
-
kiln_ai/adapters/fine_tune/fireworks_finetune.py,sha256=
|
|
28
|
+
kiln_ai/adapters/fine_tune/base_finetune.py,sha256=g-lWuZMkOj2djcczuHke_Ai7Z7RPg41AFSgoxHgsw3U,5889
|
|
29
|
+
kiln_ai/adapters/fine_tune/dataset_formatter.py,sha256=ky48er7lMIS3Kv5WflaLpUDvWiVGYgl8QlI0M_wy6Vo,14409
|
|
30
|
+
kiln_ai/adapters/fine_tune/finetune_registry.py,sha256=9RJLjviSoN3dQnKJE9Ss7df7dtdJgbuShB8IUcI-q9k,726
|
|
31
|
+
kiln_ai/adapters/fine_tune/fireworks_finetune.py,sha256=ze0QxghpHAqwO9nXOTkDEC9irmqduX5bjIhZDU0DCZQ,20101
|
|
33
32
|
kiln_ai/adapters/fine_tune/openai_finetune.py,sha256=Dz9E_0BWfrIkvv8ArZe-RKPwbIKPZ3v8rfbc3JELyTY,8571
|
|
34
|
-
kiln_ai/adapters/fine_tune/test_base_finetune.py,sha256=
|
|
35
|
-
kiln_ai/adapters/fine_tune/test_dataset_formatter.py,sha256=
|
|
36
|
-
kiln_ai/adapters/fine_tune/test_fireworks_tinetune.py,sha256=
|
|
33
|
+
kiln_ai/adapters/fine_tune/test_base_finetune.py,sha256=Tq0Klw7ou5_6H_bouTbI3PxYw7H30K32wlgWJE_luYk,10751
|
|
34
|
+
kiln_ai/adapters/fine_tune/test_dataset_formatter.py,sha256=kUGn2kv2jwosuabuhYgA3oXJXAdqK1AAaJI496ScOGY,24015
|
|
35
|
+
kiln_ai/adapters/fine_tune/test_fireworks_tinetune.py,sha256=NCl2U6ZqqJ8dnysGGmfir9RGcV-StPtoi5cetRjW6Zc,36754
|
|
37
36
|
kiln_ai/adapters/fine_tune/test_openai_finetune.py,sha256=H63Xk2PNHbt5Ev5IQpdR9JZ4uz-Huo2gfuC4mHHqe0w,20011
|
|
38
37
|
kiln_ai/adapters/fine_tune/test_together_finetune.py,sha256=BUJFsyq_g77gU0JN3hg6FMBvqb0DIyTeAek-wxomKIg,18090
|
|
38
|
+
kiln_ai/adapters/fine_tune/test_vertex_finetune.py,sha256=rAmcQJNPXqRacxg6RzjEQ8FNLKCp9qZRHToH7fm-7W0,19214
|
|
39
39
|
kiln_ai/adapters/fine_tune/together_finetune.py,sha256=EbMPsTyKMubfwOalkFLiNFlMFIRKxLibzMTyLeUkle4,14010
|
|
40
|
+
kiln_ai/adapters/fine_tune/vertex_finetune.py,sha256=Ik6Ov711-oruJnMHpVZTPimWJY2W_JnfdKIdR2djGrc,8545
|
|
40
41
|
kiln_ai/adapters/model_adapters/__init__.py,sha256=m5GRtOHwVVvp_XDOss8c1X3NFf1wQQlC2eBgI4tXQhM,212
|
|
41
|
-
kiln_ai/adapters/model_adapters/base_adapter.py,sha256=
|
|
42
|
-
kiln_ai/adapters/model_adapters/litellm_adapter.py,sha256=
|
|
42
|
+
kiln_ai/adapters/model_adapters/base_adapter.py,sha256=ishm_oVTNxSDC0GPrydHnyOPqp_U4XiTOx0-iI2fEiU,10433
|
|
43
|
+
kiln_ai/adapters/model_adapters/litellm_adapter.py,sha256=pbXFfJckyvptp577-YXGMG2hltYMFQrUT97PsSWa2KQ,16437
|
|
43
44
|
kiln_ai/adapters/model_adapters/litellm_config.py,sha256=7-tIh5cuVu23Uy2Sd6q7UCT_4VgevBsAzVhQMj6Svgw,425
|
|
44
45
|
kiln_ai/adapters/model_adapters/test_base_adapter.py,sha256=uQyKrHLN3Jha6R-6SWkEME6brQecVFdPTSXogo-xpt0,6556
|
|
45
46
|
kiln_ai/adapters/model_adapters/test_litellm_adapter.py,sha256=QpnzuReNeBzvvRYnNj_5c8l1PS7NyrDDUQx_o21IIH4,13731
|
|
@@ -63,7 +64,7 @@ kiln_ai/datamodel/dataset_filters.py,sha256=hWKxGJ-mSl4y0igyNcpmRoRYCiGrf0_uN4MM
|
|
|
63
64
|
kiln_ai/datamodel/dataset_split.py,sha256=q4l4SlUvjLV547bzk7Z-fbmj_o26GDcYOZ2rA5RPh3c,5612
|
|
64
65
|
kiln_ai/datamodel/eval.py,sha256=kio2LqQ87MsP75DJTiIVdVfopTZXH4xjGN9g11V1mUU,13826
|
|
65
66
|
kiln_ai/datamodel/finetune.py,sha256=TYoNVRAfbjqvrY-1YmHwG6xSoDljiJWuuVcTbvQAJL4,4569
|
|
66
|
-
kiln_ai/datamodel/json_schema.py,sha256=
|
|
67
|
+
kiln_ai/datamodel/json_schema.py,sha256=o50wSp8frRXjT-NZjml4-Is7LNoF7DQP4g3AaaYzBfI,3379
|
|
67
68
|
kiln_ai/datamodel/model_cache.py,sha256=9X4aAigbkFdytckgw8InCMh86uBna0ME_1HJSeMPEn0,4495
|
|
68
69
|
kiln_ai/datamodel/project.py,sha256=uVH2_3TDFtsG_tpts81A-zbd9uPDFxAwMCKZt_km3IE,727
|
|
69
70
|
kiln_ai/datamodel/prompt.py,sha256=70JPYHfgyX18cHW_DXoMzIOA28Jbaz6gyabElmpycyc,1161
|
|
@@ -71,17 +72,17 @@ kiln_ai/datamodel/prompt_id.py,sha256=eU2TV0RZapn-BgnZ4sOSNOOVEQ3aPaLzW4YSYCd3OB
|
|
|
71
72
|
kiln_ai/datamodel/registry.py,sha256=XwGFXJFKZtOpR1Z9ven6SftggfADdZRm8TFxCEVtfUQ,957
|
|
72
73
|
kiln_ai/datamodel/strict_mode.py,sha256=sm4Xka8mnJHCShtbh6MMU5dDQv-cLj8lHgHkmFKpsl0,849
|
|
73
74
|
kiln_ai/datamodel/task.py,sha256=r-_zgrQCIiIkN8gvBISdU449Z9oKp7E1XL0lkik_rVI,7036
|
|
74
|
-
kiln_ai/datamodel/task_output.py,sha256=
|
|
75
|
-
kiln_ai/datamodel/task_run.py,sha256=
|
|
75
|
+
kiln_ai/datamodel/task_output.py,sha256=PqI7Lyeox5lh9mItMOtpqP9Rk_K9dyMltKYu1c2p7A4,13125
|
|
76
|
+
kiln_ai/datamodel/task_run.py,sha256=mVKmHn90iPmwXGja7TNgDA3iFzXBlamJ_6KndRPkhRA,7745
|
|
76
77
|
kiln_ai/datamodel/test_basemodel.py,sha256=sJ8wXGef2WxzbrbMTYgrOwmkd5J6sHkly-cQBO2IZh4,18126
|
|
77
78
|
kiln_ai/datamodel/test_dataset_filters.py,sha256=v88QPkIsq4diUmoUF3-qj5KAW2rLRp0KDAm_pexbFy4,1894
|
|
78
79
|
kiln_ai/datamodel/test_dataset_split.py,sha256=5CHO1Lq4xQBB72tV2SPER7OZODJNvj15qxi_cYBV2Rs,11157
|
|
79
80
|
kiln_ai/datamodel/test_datasource.py,sha256=H4Kc-Im9eM7WnADWZXdoiOIrOl05RtkyuhTCKiRimyU,3905
|
|
80
81
|
kiln_ai/datamodel/test_eval_model.py,sha256=J7MqwWBgPpeXGqh3IacVUUHdZFJSZ2MgTsUNu-hNOJw,19528
|
|
81
|
-
kiln_ai/datamodel/test_example_models.py,sha256=
|
|
82
|
-
kiln_ai/datamodel/test_json_schema.py,sha256=
|
|
82
|
+
kiln_ai/datamodel/test_example_models.py,sha256=nrr13ZseFn-OVGa9bjCwoVHTVqydy0O0yJah4QiqqbU,24326
|
|
83
|
+
kiln_ai/datamodel/test_json_schema.py,sha256=R0Cfc9WbieMslgvYsj2HFx8RHIq2fF9NcT5jH-kEqh4,4793
|
|
83
84
|
kiln_ai/datamodel/test_model_cache.py,sha256=Fy-ucYNzS5JEG-8SFY4nVHA8iRbXXxai20f8_oGl97o,8184
|
|
84
|
-
kiln_ai/datamodel/test_model_perf.py,sha256=
|
|
85
|
+
kiln_ai/datamodel/test_model_perf.py,sha256=9_76f__5XtZSHSjuaoiHRj2t-z3OWn-sSA4S9kH1jpY,3306
|
|
85
86
|
kiln_ai/datamodel/test_models.py,sha256=hmV7sTbOamWJCwOY96w-g4PQRv4Uai-XaHtg0QKH-ak,19295
|
|
86
87
|
kiln_ai/datamodel/test_nested_save.py,sha256=xciCddqvPyKyoyjC5Lx_3Kh1t4LJv1xYRAPazR3SRcs,5588
|
|
87
88
|
kiln_ai/datamodel/test_output_rating.py,sha256=zvPIp2shAgCs2RQBgwYoL09fRA3krHvgAqUa91RlWR0,15125
|
|
@@ -97,7 +98,7 @@ kiln_ai/utils/name_generator.py,sha256=v26TgpCwQbhQFcZvzgjZvURinjrOyyFhxpsI6NQrH
|
|
|
97
98
|
kiln_ai/utils/test_config.py,sha256=Jw3nMFeIgZUsZDRJJY2HpB-2EkR2NoZ-rDe_o9oA7ws,9174
|
|
98
99
|
kiln_ai/utils/test_dataset_import.py,sha256=ZZOt7zqtaEIlMMx0VNXyRegDvnVqbWY2bcz-iMY_Oag,17427
|
|
99
100
|
kiln_ai/utils/test_name_geneator.py,sha256=9-hSTBshyakqlPbFnNcggwLrL7lcPTitauBYHg9jFWI,1513
|
|
100
|
-
kiln_ai-0.
|
|
101
|
-
kiln_ai-0.
|
|
102
|
-
kiln_ai-0.
|
|
103
|
-
kiln_ai-0.
|
|
101
|
+
kiln_ai-0.15.0.dist-info/METADATA,sha256=80EooOjbu5b-7YgyfnOY9uYjFk8bo5czNWm3QgWaFys,12263
|
|
102
|
+
kiln_ai-0.15.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
103
|
+
kiln_ai-0.15.0.dist-info/licenses/LICENSE.txt,sha256=_NA5pnTYgRRr4qH6lE3X-TuZJ8iRcMUi5ASoGr-lEx8,1209
|
|
104
|
+
kiln_ai-0.15.0.dist-info/RECORD,,
|