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.

Files changed (158) hide show
  1. kiln_ai/adapters/__init__.py +8 -2
  2. kiln_ai/adapters/adapter_registry.py +43 -208
  3. kiln_ai/adapters/chat/chat_formatter.py +8 -12
  4. kiln_ai/adapters/chat/test_chat_formatter.py +6 -2
  5. kiln_ai/adapters/chunkers/__init__.py +13 -0
  6. kiln_ai/adapters/chunkers/base_chunker.py +42 -0
  7. kiln_ai/adapters/chunkers/chunker_registry.py +16 -0
  8. kiln_ai/adapters/chunkers/fixed_window_chunker.py +39 -0
  9. kiln_ai/adapters/chunkers/helpers.py +23 -0
  10. kiln_ai/adapters/chunkers/test_base_chunker.py +63 -0
  11. kiln_ai/adapters/chunkers/test_chunker_registry.py +28 -0
  12. kiln_ai/adapters/chunkers/test_fixed_window_chunker.py +346 -0
  13. kiln_ai/adapters/chunkers/test_helpers.py +75 -0
  14. kiln_ai/adapters/data_gen/test_data_gen_task.py +9 -3
  15. kiln_ai/adapters/docker_model_runner_tools.py +119 -0
  16. kiln_ai/adapters/embedding/__init__.py +0 -0
  17. kiln_ai/adapters/embedding/base_embedding_adapter.py +44 -0
  18. kiln_ai/adapters/embedding/embedding_registry.py +32 -0
  19. kiln_ai/adapters/embedding/litellm_embedding_adapter.py +199 -0
  20. kiln_ai/adapters/embedding/test_base_embedding_adapter.py +283 -0
  21. kiln_ai/adapters/embedding/test_embedding_registry.py +166 -0
  22. kiln_ai/adapters/embedding/test_litellm_embedding_adapter.py +1149 -0
  23. kiln_ai/adapters/eval/base_eval.py +2 -2
  24. kiln_ai/adapters/eval/eval_runner.py +9 -3
  25. kiln_ai/adapters/eval/g_eval.py +2 -2
  26. kiln_ai/adapters/eval/test_base_eval.py +2 -4
  27. kiln_ai/adapters/eval/test_g_eval.py +4 -5
  28. kiln_ai/adapters/extractors/__init__.py +18 -0
  29. kiln_ai/adapters/extractors/base_extractor.py +72 -0
  30. kiln_ai/adapters/extractors/encoding.py +20 -0
  31. kiln_ai/adapters/extractors/extractor_registry.py +44 -0
  32. kiln_ai/adapters/extractors/extractor_runner.py +112 -0
  33. kiln_ai/adapters/extractors/litellm_extractor.py +386 -0
  34. kiln_ai/adapters/extractors/test_base_extractor.py +244 -0
  35. kiln_ai/adapters/extractors/test_encoding.py +54 -0
  36. kiln_ai/adapters/extractors/test_extractor_registry.py +181 -0
  37. kiln_ai/adapters/extractors/test_extractor_runner.py +181 -0
  38. kiln_ai/adapters/extractors/test_litellm_extractor.py +1192 -0
  39. kiln_ai/adapters/fine_tune/__init__.py +1 -1
  40. kiln_ai/adapters/fine_tune/openai_finetune.py +14 -4
  41. kiln_ai/adapters/fine_tune/test_dataset_formatter.py +2 -2
  42. kiln_ai/adapters/fine_tune/test_fireworks_tinetune.py +2 -6
  43. kiln_ai/adapters/fine_tune/test_openai_finetune.py +108 -111
  44. kiln_ai/adapters/fine_tune/test_together_finetune.py +2 -6
  45. kiln_ai/adapters/ml_embedding_model_list.py +192 -0
  46. kiln_ai/adapters/ml_model_list.py +761 -37
  47. kiln_ai/adapters/model_adapters/base_adapter.py +51 -21
  48. kiln_ai/adapters/model_adapters/litellm_adapter.py +380 -138
  49. kiln_ai/adapters/model_adapters/test_base_adapter.py +193 -17
  50. kiln_ai/adapters/model_adapters/test_litellm_adapter.py +407 -2
  51. kiln_ai/adapters/model_adapters/test_litellm_adapter_tools.py +1103 -0
  52. kiln_ai/adapters/model_adapters/test_saving_adapter_results.py +5 -5
  53. kiln_ai/adapters/model_adapters/test_structured_output.py +113 -5
  54. kiln_ai/adapters/ollama_tools.py +69 -12
  55. kiln_ai/adapters/parsers/__init__.py +1 -1
  56. kiln_ai/adapters/provider_tools.py +205 -47
  57. kiln_ai/adapters/rag/deduplication.py +49 -0
  58. kiln_ai/adapters/rag/progress.py +252 -0
  59. kiln_ai/adapters/rag/rag_runners.py +844 -0
  60. kiln_ai/adapters/rag/test_deduplication.py +195 -0
  61. kiln_ai/adapters/rag/test_progress.py +785 -0
  62. kiln_ai/adapters/rag/test_rag_runners.py +2376 -0
  63. kiln_ai/adapters/remote_config.py +80 -8
  64. kiln_ai/adapters/repair/test_repair_task.py +12 -9
  65. kiln_ai/adapters/run_output.py +3 -0
  66. kiln_ai/adapters/test_adapter_registry.py +657 -85
  67. kiln_ai/adapters/test_docker_model_runner_tools.py +305 -0
  68. kiln_ai/adapters/test_ml_embedding_model_list.py +429 -0
  69. kiln_ai/adapters/test_ml_model_list.py +251 -1
  70. kiln_ai/adapters/test_ollama_tools.py +340 -1
  71. kiln_ai/adapters/test_prompt_adaptors.py +13 -6
  72. kiln_ai/adapters/test_prompt_builders.py +1 -1
  73. kiln_ai/adapters/test_provider_tools.py +254 -8
  74. kiln_ai/adapters/test_remote_config.py +651 -58
  75. kiln_ai/adapters/vector_store/__init__.py +1 -0
  76. kiln_ai/adapters/vector_store/base_vector_store_adapter.py +83 -0
  77. kiln_ai/adapters/vector_store/lancedb_adapter.py +389 -0
  78. kiln_ai/adapters/vector_store/test_base_vector_store.py +160 -0
  79. kiln_ai/adapters/vector_store/test_lancedb_adapter.py +1841 -0
  80. kiln_ai/adapters/vector_store/test_vector_store_registry.py +199 -0
  81. kiln_ai/adapters/vector_store/vector_store_registry.py +33 -0
  82. kiln_ai/datamodel/__init__.py +39 -34
  83. kiln_ai/datamodel/basemodel.py +170 -1
  84. kiln_ai/datamodel/chunk.py +158 -0
  85. kiln_ai/datamodel/datamodel_enums.py +28 -0
  86. kiln_ai/datamodel/embedding.py +64 -0
  87. kiln_ai/datamodel/eval.py +1 -1
  88. kiln_ai/datamodel/external_tool_server.py +298 -0
  89. kiln_ai/datamodel/extraction.py +303 -0
  90. kiln_ai/datamodel/json_schema.py +25 -10
  91. kiln_ai/datamodel/project.py +40 -1
  92. kiln_ai/datamodel/rag.py +79 -0
  93. kiln_ai/datamodel/registry.py +0 -15
  94. kiln_ai/datamodel/run_config.py +62 -0
  95. kiln_ai/datamodel/task.py +2 -77
  96. kiln_ai/datamodel/task_output.py +6 -1
  97. kiln_ai/datamodel/task_run.py +41 -0
  98. kiln_ai/datamodel/test_attachment.py +649 -0
  99. kiln_ai/datamodel/test_basemodel.py +4 -4
  100. kiln_ai/datamodel/test_chunk_models.py +317 -0
  101. kiln_ai/datamodel/test_dataset_split.py +1 -1
  102. kiln_ai/datamodel/test_embedding_models.py +448 -0
  103. kiln_ai/datamodel/test_eval_model.py +6 -6
  104. kiln_ai/datamodel/test_example_models.py +175 -0
  105. kiln_ai/datamodel/test_external_tool_server.py +691 -0
  106. kiln_ai/datamodel/test_extraction_chunk.py +206 -0
  107. kiln_ai/datamodel/test_extraction_model.py +470 -0
  108. kiln_ai/datamodel/test_rag.py +641 -0
  109. kiln_ai/datamodel/test_registry.py +8 -3
  110. kiln_ai/datamodel/test_task.py +15 -47
  111. kiln_ai/datamodel/test_tool_id.py +320 -0
  112. kiln_ai/datamodel/test_vector_store.py +320 -0
  113. kiln_ai/datamodel/tool_id.py +105 -0
  114. kiln_ai/datamodel/vector_store.py +141 -0
  115. kiln_ai/tools/__init__.py +8 -0
  116. kiln_ai/tools/base_tool.py +82 -0
  117. kiln_ai/tools/built_in_tools/__init__.py +13 -0
  118. kiln_ai/tools/built_in_tools/math_tools.py +124 -0
  119. kiln_ai/tools/built_in_tools/test_math_tools.py +204 -0
  120. kiln_ai/tools/mcp_server_tool.py +95 -0
  121. kiln_ai/tools/mcp_session_manager.py +246 -0
  122. kiln_ai/tools/rag_tools.py +157 -0
  123. kiln_ai/tools/test_base_tools.py +199 -0
  124. kiln_ai/tools/test_mcp_server_tool.py +457 -0
  125. kiln_ai/tools/test_mcp_session_manager.py +1585 -0
  126. kiln_ai/tools/test_rag_tools.py +848 -0
  127. kiln_ai/tools/test_tool_registry.py +562 -0
  128. kiln_ai/tools/tool_registry.py +85 -0
  129. kiln_ai/utils/__init__.py +3 -0
  130. kiln_ai/utils/async_job_runner.py +62 -17
  131. kiln_ai/utils/config.py +24 -2
  132. kiln_ai/utils/env.py +15 -0
  133. kiln_ai/utils/filesystem.py +14 -0
  134. kiln_ai/utils/filesystem_cache.py +60 -0
  135. kiln_ai/utils/litellm.py +94 -0
  136. kiln_ai/utils/lock.py +100 -0
  137. kiln_ai/utils/mime_type.py +38 -0
  138. kiln_ai/utils/open_ai_types.py +94 -0
  139. kiln_ai/utils/pdf_utils.py +38 -0
  140. kiln_ai/utils/project_utils.py +17 -0
  141. kiln_ai/utils/test_async_job_runner.py +151 -35
  142. kiln_ai/utils/test_config.py +138 -1
  143. kiln_ai/utils/test_env.py +142 -0
  144. kiln_ai/utils/test_filesystem_cache.py +316 -0
  145. kiln_ai/utils/test_litellm.py +206 -0
  146. kiln_ai/utils/test_lock.py +185 -0
  147. kiln_ai/utils/test_mime_type.py +66 -0
  148. kiln_ai/utils/test_open_ai_types.py +131 -0
  149. kiln_ai/utils/test_pdf_utils.py +73 -0
  150. kiln_ai/utils/test_uuid.py +111 -0
  151. kiln_ai/utils/test_validation.py +524 -0
  152. kiln_ai/utils/uuid.py +9 -0
  153. kiln_ai/utils/validation.py +90 -0
  154. {kiln_ai-0.19.0.dist-info → kiln_ai-0.21.0.dist-info}/METADATA +12 -5
  155. kiln_ai-0.21.0.dist-info/RECORD +211 -0
  156. kiln_ai-0.19.0.dist-info/RECORD +0 -115
  157. {kiln_ai-0.19.0.dist-info → kiln_ai-0.21.0.dist-info}/WHEEL +0 -0
  158. {kiln_ai-0.19.0.dist-info → kiln_ai-0.21.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,305 @@
1
+ from unittest.mock import AsyncMock, Mock, patch
2
+
3
+ import httpx
4
+ import openai
5
+ import pytest
6
+
7
+ from kiln_ai.adapters.docker_model_runner_tools import (
8
+ DockerModelRunnerConnection,
9
+ docker_model_runner_base_url,
10
+ parse_docker_model_runner_models,
11
+ )
12
+ from kiln_ai.datamodel.datamodel_enums import ModelProviderName
13
+
14
+
15
+ def test_docker_model_runner_base_url_default():
16
+ """Test that the default base URL is returned when no config is set."""
17
+ with patch("kiln_ai.adapters.docker_model_runner_tools.Config") as mock_config:
18
+ mock_config.shared().docker_model_runner_base_url = None
19
+ result = docker_model_runner_base_url()
20
+ assert result == "http://localhost:12434/engines/llama.cpp"
21
+
22
+
23
+ def test_docker_model_runner_base_url_from_config():
24
+ """Test that the configured base URL is returned when set."""
25
+ with patch("kiln_ai.adapters.docker_model_runner_tools.Config") as mock_config:
26
+ mock_config.shared().docker_model_runner_base_url = (
27
+ "http://custom:8080/engines/llama.cpp"
28
+ )
29
+ result = docker_model_runner_base_url()
30
+ assert result == "http://custom:8080/engines/llama.cpp"
31
+
32
+
33
+ def test_parse_docker_model_runner_models_with_supported_models():
34
+ """Test parsing Docker Model Runner models response with supported models."""
35
+ # Create mock OpenAI Model objects
36
+ mock_models = [
37
+ Mock(id="ai/llama3.2:3B-Q4_K_M"),
38
+ Mock(id="ai/qwen3:8B-Q4_K_M"),
39
+ Mock(id="ai/gemma3n:4B-Q4_K_M"),
40
+ Mock(id="unsupported-model"),
41
+ ]
42
+
43
+ with patch(
44
+ "kiln_ai.adapters.docker_model_runner_tools.built_in_models"
45
+ ) as mock_built_in_models:
46
+ # Mock built-in models with Docker Model Runner providers
47
+ mock_model = Mock()
48
+ mock_provider = Mock()
49
+ mock_provider.name = ModelProviderName.docker_model_runner
50
+ mock_provider.model_id = "ai/llama3.2:3B-Q4_K_M"
51
+ mock_model.providers = [mock_provider]
52
+ mock_built_in_models.__iter__ = Mock(return_value=iter([mock_model]))
53
+
54
+ result = parse_docker_model_runner_models(mock_models) # type: ignore
55
+
56
+ assert result is not None
57
+ assert result.message == "Docker Model Runner connected"
58
+ assert "ai/llama3.2:3B-Q4_K_M" in result.supported_models
59
+ assert "unsupported-model" in result.untested_models
60
+
61
+
62
+ def test_parse_docker_model_runner_models_no_models():
63
+ """Test parsing Docker Model Runner models response with no models."""
64
+ mock_models = []
65
+
66
+ result = parse_docker_model_runner_models(mock_models)
67
+
68
+ assert result is not None
69
+ assert "no supported models are available" in result.message
70
+ assert len(result.supported_models) == 0
71
+ assert len(result.untested_models) == 0
72
+
73
+
74
+ def test_docker_model_runner_connection_all_models():
75
+ """Test that DockerModelRunnerConnection.all_models() returns both supported and untested models."""
76
+ connection = DockerModelRunnerConnection(
77
+ message="Test",
78
+ supported_models=["model1", "model2"],
79
+ untested_models=["model3", "model4"],
80
+ )
81
+
82
+ all_models = connection.all_models()
83
+ assert all_models == ["model1", "model2", "model3", "model4"]
84
+
85
+
86
+ @pytest.mark.asyncio
87
+ async def test_docker_model_runner_online_success():
88
+ """Test that docker_model_runner_online returns True when service is available."""
89
+ with patch(
90
+ "kiln_ai.adapters.docker_model_runner_tools.httpx.AsyncClient"
91
+ ) as mock_client_class:
92
+ mock_client = Mock()
93
+ mock_response = Mock()
94
+ mock_response.raise_for_status.return_value = None
95
+ mock_client.get = AsyncMock(return_value=mock_response)
96
+ mock_client_class.return_value.__aenter__.return_value = mock_client
97
+
98
+ from kiln_ai.adapters.docker_model_runner_tools import (
99
+ docker_model_runner_online,
100
+ )
101
+
102
+ result = await docker_model_runner_online()
103
+
104
+ assert result is True
105
+ mock_client.get.assert_called_once()
106
+
107
+
108
+ @pytest.mark.asyncio
109
+ async def test_docker_model_runner_online_failure():
110
+ """Test that docker_model_runner_online returns False when service is unavailable."""
111
+ with patch(
112
+ "kiln_ai.adapters.docker_model_runner_tools.httpx.AsyncClient"
113
+ ) as mock_client_class:
114
+ mock_client = Mock()
115
+ mock_client.get = AsyncMock(side_effect=httpx.RequestError("Connection error"))
116
+ mock_client_class.return_value.__aenter__.return_value = mock_client
117
+
118
+ from kiln_ai.adapters.docker_model_runner_tools import (
119
+ docker_model_runner_online,
120
+ )
121
+
122
+ result = await docker_model_runner_online()
123
+
124
+ assert result is False
125
+
126
+
127
+ @pytest.mark.asyncio
128
+ async def test_get_docker_model_runner_connection_success():
129
+ """Test get_docker_model_runner_connection with successful connection."""
130
+ from kiln_ai.adapters.docker_model_runner_tools import (
131
+ get_docker_model_runner_connection,
132
+ )
133
+
134
+ # Mock OpenAI client and models response
135
+ mock_model = Mock()
136
+ mock_model.id = "ai/llama3.2:3B-Q4_K_M"
137
+ mock_models_response = [mock_model]
138
+
139
+ with (
140
+ patch(
141
+ "kiln_ai.adapters.docker_model_runner_tools.openai.OpenAI"
142
+ ) as mock_openai,
143
+ patch(
144
+ "kiln_ai.adapters.docker_model_runner_tools.parse_docker_model_runner_models"
145
+ ) as mock_parse,
146
+ patch(
147
+ "kiln_ai.adapters.docker_model_runner_tools.docker_model_runner_base_url"
148
+ ) as mock_base_url,
149
+ ):
150
+ mock_base_url.return_value = "http://localhost:12434/engines"
151
+ mock_client = Mock()
152
+ mock_client.models.list.return_value = mock_models_response
153
+ mock_openai.return_value = mock_client
154
+
155
+ expected_connection = DockerModelRunnerConnection(
156
+ message="Connected",
157
+ supported_models=["ai/llama3.2:3B-Q4_K_M"],
158
+ untested_models=[],
159
+ )
160
+ mock_parse.return_value = expected_connection
161
+
162
+ result = await get_docker_model_runner_connection()
163
+
164
+ assert result == expected_connection
165
+ mock_openai.assert_called_once_with(
166
+ api_key="dummy",
167
+ base_url="http://localhost:12434/engines/v1",
168
+ max_retries=0,
169
+ )
170
+ mock_parse.assert_called_once_with(mock_models_response)
171
+
172
+
173
+ @pytest.mark.asyncio
174
+ async def test_get_docker_model_runner_connection_with_custom_url():
175
+ """Test get_docker_model_runner_connection with custom URL."""
176
+ from kiln_ai.adapters.docker_model_runner_tools import (
177
+ get_docker_model_runner_connection,
178
+ )
179
+
180
+ # Mock OpenAI client and models response
181
+ mock_model = Mock()
182
+ mock_model.id = "ai/llama3.2:3B-Q4_K_M"
183
+ mock_models_response = [mock_model]
184
+
185
+ with (
186
+ patch(
187
+ "kiln_ai.adapters.docker_model_runner_tools.openai.OpenAI"
188
+ ) as mock_openai,
189
+ patch(
190
+ "kiln_ai.adapters.docker_model_runner_tools.parse_docker_model_runner_models"
191
+ ) as mock_parse,
192
+ ):
193
+ mock_client = Mock()
194
+ mock_client.models.list.return_value = mock_models_response
195
+ mock_openai.return_value = mock_client
196
+
197
+ expected_connection = DockerModelRunnerConnection(
198
+ message="Connected",
199
+ supported_models=["ai/llama3.2:3B-Q4_K_M"],
200
+ untested_models=[],
201
+ )
202
+ mock_parse.return_value = expected_connection
203
+
204
+ custom_url = "http://custom:8080/engines/llama.cpp"
205
+ result = await get_docker_model_runner_connection(custom_url)
206
+
207
+ assert result == expected_connection
208
+ mock_openai.assert_called_once_with(
209
+ api_key="dummy",
210
+ base_url=f"{custom_url}/v1",
211
+ max_retries=0,
212
+ )
213
+ mock_parse.assert_called_once_with(mock_models_response)
214
+
215
+
216
+ @pytest.mark.asyncio
217
+ async def test_get_docker_model_runner_connection_api_error():
218
+ """Test get_docker_model_runner_connection with API error."""
219
+ from kiln_ai.adapters.docker_model_runner_tools import (
220
+ get_docker_model_runner_connection,
221
+ )
222
+
223
+ with patch(
224
+ "kiln_ai.adapters.docker_model_runner_tools.openai.OpenAI"
225
+ ) as mock_openai:
226
+ mock_client = Mock()
227
+ mock_client.models.list.side_effect = openai.APIConnectionError(request=Mock())
228
+ mock_openai.return_value = mock_client
229
+
230
+ result = await get_docker_model_runner_connection()
231
+
232
+ assert result is None
233
+
234
+
235
+ @pytest.mark.asyncio
236
+ async def test_get_docker_model_runner_connection_connection_error():
237
+ """Test get_docker_model_runner_connection with connection error."""
238
+ from kiln_ai.adapters.docker_model_runner_tools import (
239
+ get_docker_model_runner_connection,
240
+ )
241
+
242
+ with patch(
243
+ "kiln_ai.adapters.docker_model_runner_tools.openai.OpenAI"
244
+ ) as mock_openai:
245
+ mock_client = Mock()
246
+ mock_client.models.list.side_effect = httpx.RequestError("Connection error")
247
+ mock_openai.return_value = mock_client
248
+
249
+ result = await get_docker_model_runner_connection()
250
+
251
+ assert result is None
252
+
253
+
254
+ @pytest.mark.asyncio
255
+ async def test_get_docker_model_runner_connection_http_error():
256
+ """Test get_docker_model_runner_connection with HTTP error."""
257
+ from kiln_ai.adapters.docker_model_runner_tools import (
258
+ get_docker_model_runner_connection,
259
+ )
260
+
261
+ with patch(
262
+ "kiln_ai.adapters.docker_model_runner_tools.openai.OpenAI"
263
+ ) as mock_openai:
264
+ mock_client = Mock()
265
+ mock_client.models.list.side_effect = httpx.RequestError("HTTP error")
266
+ mock_openai.return_value = mock_client
267
+
268
+ result = await get_docker_model_runner_connection()
269
+
270
+ assert result is None
271
+
272
+
273
+ def test_docker_model_runner_model_installed_true():
274
+ """Test docker_model_runner_model_installed returns True when model is installed."""
275
+ from kiln_ai.adapters.docker_model_runner_tools import (
276
+ docker_model_runner_model_installed,
277
+ )
278
+
279
+ connection = DockerModelRunnerConnection(
280
+ message="Test",
281
+ supported_models=["model1", "model2"],
282
+ untested_models=["model3", "model4"],
283
+ )
284
+
285
+ # Test model in supported_models
286
+ assert docker_model_runner_model_installed(connection, "model1") is True
287
+
288
+ # Test model in untested_models
289
+ assert docker_model_runner_model_installed(connection, "model3") is True
290
+
291
+
292
+ def test_docker_model_runner_model_installed_false():
293
+ """Test docker_model_runner_model_installed returns False when model is not installed."""
294
+ from kiln_ai.adapters.docker_model_runner_tools import (
295
+ docker_model_runner_model_installed,
296
+ )
297
+
298
+ connection = DockerModelRunnerConnection(
299
+ message="Test",
300
+ supported_models=["model1", "model2"],
301
+ untested_models=["model3", "model4"],
302
+ )
303
+
304
+ # Test model not in any list
305
+ assert docker_model_runner_model_installed(connection, "nonexistent_model") is False