kiln-ai 0.5.3__tar.gz → 0.5.5__tar.gz

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 (78) hide show
  1. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/.gitignore +1 -0
  2. kiln_ai-0.5.5/LICENSE.txt +13 -0
  3. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/PKG-INFO +43 -6
  4. kiln_ai-0.5.5/README.md +58 -0
  5. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/langchain_adapters.py +29 -13
  6. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/ml_model_list.py +241 -39
  7. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/test_ml_model_list.py +26 -0
  8. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/test_prompt_adaptors.py +18 -2
  9. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/test_structured_output.py +23 -16
  10. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/pyproject.toml +4 -2
  11. kiln_ai-0.5.3/LICENSE.txt +0 -4
  12. kiln_ai-0.5.3/POET +0 -29
  13. kiln_ai-0.5.3/README.md +0 -23
  14. kiln_ai-0.5.3/kiln_ai.egg-info/PKG-INFO +0 -45
  15. kiln_ai-0.5.3/kiln_ai.egg-info/SOURCES.txt +0 -38
  16. kiln_ai-0.5.3/kiln_ai.egg-info/dependency_links.txt +0 -1
  17. kiln_ai-0.5.3/kiln_ai.egg-info/requires.txt +0 -11
  18. kiln_ai-0.5.3/kiln_ai.egg-info/top_level.txt +0 -1
  19. kiln_ai-0.5.3/poetry.lock +0 -101
  20. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/.python-version +0 -0
  21. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/index.html +0 -0
  22. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/adapters/base_adapter.html +0 -0
  23. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/adapters/langchain_adapters.html +0 -0
  24. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/adapters/ml_model_list.html +0 -0
  25. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/adapters/prompt_builders.html +0 -0
  26. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/adapters/repair/repair_task.html +0 -0
  27. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/adapters/repair.html +0 -0
  28. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/adapters.html +0 -0
  29. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/datamodel/basemodel.html +0 -0
  30. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/datamodel/json_schema.html +0 -0
  31. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/datamodel.html +0 -0
  32. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/utils/config.html +0 -0
  33. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/utils/formatting.html +0 -0
  34. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai/utils.html +0 -0
  35. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/kiln_ai.html +0 -0
  36. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/core_library_docs/search.js +0 -0
  37. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/index.html +0 -0
  38. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/adapters/base_adapter.html +0 -0
  39. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/adapters/langchain_adapters.html +0 -0
  40. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/adapters/ml_model_list.html +0 -0
  41. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/adapters/prompt_builders.html +0 -0
  42. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/adapters/repair/repair_task.html +0 -0
  43. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/adapters/repair.html +0 -0
  44. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/adapters.html +0 -0
  45. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/datamodel/basemodel.html +0 -0
  46. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/datamodel/json_schema.html +0 -0
  47. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/datamodel.html +0 -0
  48. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/utils/config.html +0 -0
  49. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/utils/formatting.html +0 -0
  50. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai/utils.html +0 -0
  51. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/kiln_ai.html +0 -0
  52. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/docs/kiln_core_docs/search.js +0 -0
  53. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/__init__.py +0 -0
  54. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/__init__.py +0 -0
  55. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/base_adapter.py +0 -0
  56. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/prompt_builders.py +0 -0
  57. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/repair/__init__.py +0 -0
  58. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/repair/repair_task.py +0 -0
  59. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/repair/test_repair_task.py +0 -0
  60. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/test_langchain_adapter.py +0 -0
  61. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/test_prompt_builders.py +0 -0
  62. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/adapters/test_saving_adapter_results.py +0 -0
  63. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/__init__.py +0 -0
  64. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/basemodel.py +0 -0
  65. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/json_schema.py +0 -0
  66. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/test_basemodel.py +0 -0
  67. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/test_datasource.py +0 -0
  68. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/test_example_models.py +0 -0
  69. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/test_json_schema.py +0 -0
  70. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/test_models.py +0 -0
  71. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/test_nested_save.py +0 -0
  72. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/datamodel/test_output_rating.py +0 -0
  73. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/utils/__init__.py +0 -0
  74. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/utils/config.py +0 -0
  75. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/utils/formatting.py +0 -0
  76. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/kiln_ai/utils/test_config.py +0 -0
  77. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/setup.cfg +0 -0
  78. {kiln_ai-0.5.3 → kiln_ai-0.5.5}/uv.lock +0 -0
@@ -7,6 +7,7 @@ __pycache__/
7
7
  *$py.class
8
8
  .coverage
9
9
  **/.venv
10
+ **/*.egg-info
10
11
 
11
12
  libs/core/docs
12
13
  libs/core/build
@@ -0,0 +1,13 @@
1
+
2
+
3
+ This license applies only to the software in the libs/core directory.
4
+
5
+ =======================================================
6
+
7
+ Copyright 2024 - Chesterfield Laboratories Inc.
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: kiln-ai
3
- Version: 0.5.3
3
+ Version: 0.5.5
4
4
  Summary: Kiln AI
5
5
  Project-URL: Homepage, https://getkiln.ai
6
6
  Project-URL: Repository, https://github.com/Kiln-AI/kiln
@@ -9,9 +9,11 @@ Project-URL: Issues, https://github.com/Kiln-AI/kiln/issues
9
9
  Author-email: "Steve Cosman, Chesterfield Laboratories Inc" <scosman@users.noreply.github.com>
10
10
  License-File: LICENSE.txt
11
11
  Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
12
13
  Classifier: Programming Language :: Python :: 3.10
13
14
  Classifier: Programming Language :: Python :: 3.11
14
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
15
17
  Requires-Python: >=3.10
16
18
  Requires-Dist: coverage>=7.6.4
17
19
  Requires-Dist: jsonschema>=4.23.0
@@ -29,6 +31,12 @@ Description-Content-Type: text/markdown
29
31
 
30
32
  # kiln_ai
31
33
 
34
+ <p align="center">
35
+ <picture>
36
+ <img width="205" alt="Kiln AI Logo" src="https://github.com/user-attachments/assets/5fbcbdf7-1feb-45c9-bd73-99a46dd0a47f">
37
+ </picture>
38
+ </p>
39
+
32
40
  [![PyPI - Version](https://img.shields.io/pypi/v/kiln-ai.svg?logo=pypi&label=PyPI&logoColor=gold)](https://pypi.org/project/kiln-ai)
33
41
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/kiln-ai.svg)](https://pypi.org/project/kiln-ai)
34
42
  [![Docs](https://img.shields.io/badge/docs-pdoc-blue)](https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html)
@@ -41,12 +49,41 @@ Description-Content-Type: text/markdown
41
49
  pip install kiln_ai
42
50
  ```
43
51
 
44
- ## About Kiln AI
52
+ ## About
53
+
54
+ This package is the Kiln AI core library. There is also a separate desktop application and server package. Learn more about Kiln AI at [getkiln.ai](https://getkiln.ai)
55
+
56
+ - Github: [github.com/Kiln-AI/kiln](https://github.com/Kiln-AI/kiln)
57
+ - Core Library Docs: [https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html](https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html)
58
+
59
+ ## Quick Start
45
60
 
46
- Learn more about Kiln AI at [getkiln.ai](https://getkiln.ai)
61
+ ```python
62
+ from kiln_ai.datamodel import Project
47
63
 
48
- This package is the Kiln AI core library. There is also a separate desktop application and server package.
64
+ print("Reading Kiln project")
65
+ project = Project.load_from_file("path/to/project.kiln")
66
+ print("Project: ", project.name, " - ", project.description)
49
67
 
50
- Github: [github.com/Kiln-AI/kiln](https://github.com/Kiln-AI/kiln)
68
+ task = project.tasks()[0]
69
+ print("Task: ", task.name, " - ", task.description)
70
+ print("Total dataset size:", len(task.runs()))
51
71
 
52
- Docs: [https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html](https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html)
72
+ # ... app specific code using the typed kiln datamodel
73
+
74
+ # Alternatively, load data into pandas or a similar tool:
75
+ import glob
76
+ import json
77
+ import pandas as pd
78
+ from pathlib import Path
79
+
80
+ dataitem_glob = str(task.path.parent) + "/runs/*/task_run.kiln"
81
+
82
+ dfs = []
83
+ for file in glob.glob(dataitem_glob):
84
+ js = json.loads(Path(file).read_text())
85
+ df = pd.json_normalize(js)
86
+ dfs.append(df)
87
+ final_df = pd.concat(dfs, ignore_index=True)
88
+ print(final_df)
89
+ ```
@@ -0,0 +1,58 @@
1
+ # kiln_ai
2
+
3
+ <p align="center">
4
+ <picture>
5
+ <img width="205" alt="Kiln AI Logo" src="https://github.com/user-attachments/assets/5fbcbdf7-1feb-45c9-bd73-99a46dd0a47f">
6
+ </picture>
7
+ </p>
8
+
9
+ [![PyPI - Version](https://img.shields.io/pypi/v/kiln-ai.svg?logo=pypi&label=PyPI&logoColor=gold)](https://pypi.org/project/kiln-ai)
10
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/kiln-ai.svg)](https://pypi.org/project/kiln-ai)
11
+ [![Docs](https://img.shields.io/badge/docs-pdoc-blue)](https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html)
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ ```console
18
+ pip install kiln_ai
19
+ ```
20
+
21
+ ## About
22
+
23
+ This package is the Kiln AI core library. There is also a separate desktop application and server package. Learn more about Kiln AI at [getkiln.ai](https://getkiln.ai)
24
+
25
+ - Github: [github.com/Kiln-AI/kiln](https://github.com/Kiln-AI/kiln)
26
+ - Core Library Docs: [https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html](https://kiln-ai.github.io/Kiln/kiln_core_docs/index.html)
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ from kiln_ai.datamodel import Project
32
+
33
+ print("Reading Kiln project")
34
+ project = Project.load_from_file("path/to/project.kiln")
35
+ print("Project: ", project.name, " - ", project.description)
36
+
37
+ task = project.tasks()[0]
38
+ print("Task: ", task.name, " - ", task.description)
39
+ print("Total dataset size:", len(task.runs()))
40
+
41
+ # ... app specific code using the typed kiln datamodel
42
+
43
+ # Alternatively, load data into pandas or a similar tool:
44
+ import glob
45
+ import json
46
+ import pandas as pd
47
+ from pathlib import Path
48
+
49
+ dataitem_glob = str(task.path.parent) + "/runs/*/task_run.kiln"
50
+
51
+ dfs = []
52
+ for file in glob.glob(dataitem_glob):
53
+ js = json.loads(Path(file).read_text())
54
+ df = pd.json_normalize(js)
55
+ dfs.append(df)
56
+ final_df = pd.concat(dfs, ignore_index=True)
57
+ print(final_df)
58
+ ```
@@ -1,16 +1,23 @@
1
1
  from typing import Dict
2
2
 
3
+ from langchain_core.language_models import LanguageModelInput
3
4
  from langchain_core.language_models.chat_models import BaseChatModel
4
5
  from langchain_core.messages import HumanMessage, SystemMessage
5
6
  from langchain_core.messages.base import BaseMessage
7
+ from langchain_core.runnables import Runnable
8
+ from pydantic import BaseModel
6
9
 
7
10
  import kiln_ai.datamodel as datamodel
8
11
 
9
12
  from .base_adapter import AdapterInfo, BaseAdapter, BasePromptBuilder
10
13
  from .ml_model_list import langchain_model_from
11
14
 
15
+ LangChainModelType = BaseChatModel | Runnable[LanguageModelInput, Dict | BaseModel]
16
+
12
17
 
13
18
  class LangChainPromptAdapter(BaseAdapter):
19
+ _model: LangChainModelType | None = None
20
+
14
21
  def __init__(
15
22
  self,
16
23
  kiln_task: datamodel.Task,
@@ -21,7 +28,7 @@ class LangChainPromptAdapter(BaseAdapter):
21
28
  ):
22
29
  super().__init__(kiln_task, prompt_builder=prompt_builder)
23
30
  if custom_model is not None:
24
- self.model = custom_model
31
+ self._model = custom_model
25
32
 
26
33
  # Attempt to infer model provider and name from custom model
27
34
  self.model_provider = "custom.langchain:" + custom_model.__class__.__name__
@@ -37,19 +44,32 @@ class LangChainPromptAdapter(BaseAdapter):
37
44
  ):
38
45
  self.model_name = "custom.langchain:" + getattr(custom_model, "model")
39
46
  elif model_name is not None:
40
- self.model = langchain_model_from(model_name, provider)
41
47
  self.model_name = model_name
42
48
  self.model_provider = provider or "custom.langchain.default_provider"
43
49
  else:
44
50
  raise ValueError(
45
51
  "model_name and provider must be provided if custom_model is not provided"
46
52
  )
53
+
54
+ def adapter_specific_instructions(self) -> str | None:
55
+ # TODO: would be better to explicitly use bind_tools:tool_choice="task_response" here
47
56
  if self.has_structured_output():
48
- if not hasattr(self.model, "with_structured_output") or not callable(
49
- getattr(self.model, "with_structured_output")
57
+ return "Always respond with a tool call. Never respond with a human readable message."
58
+ return None
59
+
60
+ async def model(self) -> LangChainModelType:
61
+ # cached model
62
+ if self._model:
63
+ return self._model
64
+
65
+ self._model = await langchain_model_from(self.model_name, self.model_provider)
66
+
67
+ if self.has_structured_output():
68
+ if not hasattr(self._model, "with_structured_output") or not callable(
69
+ getattr(self._model, "with_structured_output")
50
70
  ):
51
71
  raise ValueError(
52
- f"model {self.model} does not support structured output, cannot use output_json_schema"
72
+ f"model {self._model} does not support structured output, cannot use output_json_schema"
53
73
  )
54
74
  # Langchain expects title/description to be at top level, on top of json schema
55
75
  output_schema = self.kiln_task.output_schema()
@@ -59,15 +79,10 @@ class LangChainPromptAdapter(BaseAdapter):
59
79
  )
60
80
  output_schema["title"] = "task_response"
61
81
  output_schema["description"] = "A response from the task"
62
- self.model = self.model.with_structured_output(
82
+ self._model = self._model.with_structured_output(
63
83
  output_schema, include_raw=True
64
84
  )
65
-
66
- def adapter_specific_instructions(self) -> str | None:
67
- # TODO: would be better to explicitly use bind_tools:tool_choice="task_response" here
68
- if self.has_structured_output():
69
- return "Always respond with a tool call. Never respond with a human readable message."
70
- return None
85
+ return self._model
71
86
 
72
87
  async def _run(self, input: Dict | str) -> Dict | str:
73
88
  prompt = self.build_prompt()
@@ -76,7 +91,8 @@ class LangChainPromptAdapter(BaseAdapter):
76
91
  SystemMessage(content=prompt),
77
92
  HumanMessage(content=user_msg),
78
93
  ]
79
- response = self.model.invoke(messages)
94
+ model = await self.model()
95
+ response = model.invoke(messages)
80
96
 
81
97
  if self.has_structured_output():
82
98
  if (
@@ -2,9 +2,10 @@ import os
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
4
  from os import getenv
5
- from typing import Dict, List, NoReturn
5
+ from typing import Any, Dict, List, NoReturn
6
6
 
7
7
  import httpx
8
+ import requests
8
9
  from langchain_aws import ChatBedrockConverse
9
10
  from langchain_core.language_models.chat_models import BaseChatModel
10
11
  from langchain_groq import ChatGroq
@@ -43,6 +44,8 @@ class ModelFamily(str, Enum):
43
44
  phi = "phi"
44
45
  mistral = "mistral"
45
46
  gemma = "gemma"
47
+ gemini = "gemini"
48
+ claude = "claude"
46
49
 
47
50
 
48
51
  # Where models have instruct and raw versions, instruct is default and raw is specified
@@ -55,6 +58,9 @@ class ModelName(str, Enum):
55
58
  llama_3_1_8b = "llama_3_1_8b"
56
59
  llama_3_1_70b = "llama_3_1_70b"
57
60
  llama_3_1_405b = "llama_3_1_405b"
61
+ llama_3_2_3b = "llama_3_2_3b"
62
+ llama_3_2_11b = "llama_3_2_11b"
63
+ llama_3_2_90b = "llama_3_2_90b"
58
64
  gpt_4o_mini = "gpt_4o_mini"
59
65
  gpt_4o = "gpt_4o"
60
66
  phi_3_5 = "phi_3_5"
@@ -63,6 +69,12 @@ class ModelName(str, Enum):
63
69
  gemma_2_2b = "gemma_2_2b"
64
70
  gemma_2_9b = "gemma_2_9b"
65
71
  gemma_2_27b = "gemma_2_27b"
72
+ claude_3_5_haiku = "claude_3_5_haiku"
73
+ claude_3_5_sonnet = "claude_3_5_sonnet"
74
+ gemini_1_5_flash = "gemini_1_5_flash"
75
+ gemini_1_5_flash_8b = "gemini_1_5_flash_8b"
76
+ gemini_1_5_pro = "gemini_1_5_pro"
77
+ nemotron_70b = "nemotron_70b"
66
78
 
67
79
 
68
80
  class KilnModelProvider(BaseModel):
@@ -132,6 +144,79 @@ built_in_models: List[KilnModel] = [
132
144
  ),
133
145
  ],
134
146
  ),
147
+ # Claude 3.5 Haiku
148
+ KilnModel(
149
+ family=ModelFamily.claude,
150
+ name=ModelName.claude_3_5_haiku,
151
+ friendly_name="Claude 3.5 Haiku",
152
+ providers=[
153
+ KilnModelProvider(
154
+ name=ModelProviderName.openrouter,
155
+ provider_options={"model": "anthropic/claude-3-5-haiku"},
156
+ ),
157
+ ],
158
+ ),
159
+ # Claude 3.5 Sonnet
160
+ KilnModel(
161
+ family=ModelFamily.claude,
162
+ name=ModelName.claude_3_5_sonnet,
163
+ friendly_name="Claude 3.5 Sonnet",
164
+ providers=[
165
+ KilnModelProvider(
166
+ name=ModelProviderName.openrouter,
167
+ provider_options={"model": "anthropic/claude-3.5-sonnet"},
168
+ ),
169
+ ],
170
+ ),
171
+ # Gemini 1.5 Pro
172
+ KilnModel(
173
+ family=ModelFamily.gemini,
174
+ name=ModelName.gemini_1_5_pro,
175
+ friendly_name="Gemini 1.5 Pro",
176
+ providers=[
177
+ KilnModelProvider(
178
+ name=ModelProviderName.openrouter,
179
+ provider_options={"model": "google/gemini-pro-1.5"},
180
+ ),
181
+ ],
182
+ ),
183
+ # Gemini 1.5 Flash
184
+ KilnModel(
185
+ family=ModelFamily.gemini,
186
+ name=ModelName.gemini_1_5_flash,
187
+ friendly_name="Gemini 1.5 Flash",
188
+ providers=[
189
+ KilnModelProvider(
190
+ name=ModelProviderName.openrouter,
191
+ provider_options={"model": "google/gemini-flash-1.5"},
192
+ ),
193
+ ],
194
+ ),
195
+ # Gemini 1.5 Flash 8B
196
+ KilnModel(
197
+ family=ModelFamily.gemini,
198
+ name=ModelName.gemini_1_5_flash_8b,
199
+ friendly_name="Gemini 1.5 Flash 8B",
200
+ providers=[
201
+ KilnModelProvider(
202
+ name=ModelProviderName.openrouter,
203
+ provider_options={"model": "google/gemini-flash-1.5-8b"},
204
+ ),
205
+ ],
206
+ ),
207
+ # Nemotron 70B
208
+ KilnModel(
209
+ family=ModelFamily.llama,
210
+ name=ModelName.nemotron_70b,
211
+ friendly_name="Nemotron 70B",
212
+ providers=[
213
+ KilnModelProvider(
214
+ name=ModelProviderName.openrouter,
215
+ supports_structured_output=False,
216
+ provider_options={"model": "nvidia/llama-3.1-nemotron-70b-instruct"},
217
+ ),
218
+ ],
219
+ ),
135
220
  # Llama 3.1-8b
136
221
  KilnModel(
137
222
  family=ModelFamily.llama,
@@ -144,6 +229,7 @@ built_in_models: List[KilnModel] = [
144
229
  ),
145
230
  KilnModelProvider(
146
231
  name=ModelProviderName.amazon_bedrock,
232
+ supports_structured_output=False,
147
233
  provider_options={
148
234
  "model": "meta.llama3-1-8b-instruct-v1:0",
149
235
  "region_name": "us-west-2", # Llama 3.1 only in west-2
@@ -151,10 +237,14 @@ built_in_models: List[KilnModel] = [
151
237
  ),
152
238
  KilnModelProvider(
153
239
  name=ModelProviderName.ollama,
154
- provider_options={"model": "llama3.1"}, # 8b is default
240
+ provider_options={
241
+ "model": "llama3.1:8b",
242
+ "model_aliases": ["llama3.1"], # 8b is default
243
+ },
155
244
  ),
156
245
  KilnModelProvider(
157
246
  name=ModelProviderName.openrouter,
247
+ supports_structured_output=False,
158
248
  provider_options={"model": "meta-llama/llama-3.1-8b-instruct"},
159
249
  ),
160
250
  ],
@@ -171,7 +261,6 @@ built_in_models: List[KilnModel] = [
171
261
  ),
172
262
  KilnModelProvider(
173
263
  name=ModelProviderName.amazon_bedrock,
174
- # TODO: this should work but a bug in the bedrock response schema
175
264
  supports_structured_output=False,
176
265
  provider_options={
177
266
  "model": "meta.llama3-1-70b-instruct-v1:0",
@@ -182,11 +271,10 @@ built_in_models: List[KilnModel] = [
182
271
  name=ModelProviderName.openrouter,
183
272
  provider_options={"model": "meta-llama/llama-3.1-70b-instruct"},
184
273
  ),
185
- # TODO: enable once tests update to check if model is available
186
- # KilnModelProvider(
187
- # provider=ModelProviders.ollama,
188
- # provider_options={"model": "llama3.1:70b"},
189
- # ),
274
+ KilnModelProvider(
275
+ name=ModelProviderName.ollama,
276
+ provider_options={"model": "llama3.1:70b"},
277
+ ),
190
278
  ],
191
279
  ),
192
280
  # Llama 3.1 405b
@@ -195,11 +283,6 @@ built_in_models: List[KilnModel] = [
195
283
  name=ModelName.llama_3_1_405b,
196
284
  friendly_name="Llama 3.1 405B",
197
285
  providers=[
198
- # TODO: bring back when groq does: https://console.groq.com/docs/models
199
- # KilnModelProvider(
200
- # name=ModelProviderName.groq,
201
- # provider_options={"model": "llama-3.1-405b-instruct-v1:0"},
202
- # ),
203
286
  KilnModelProvider(
204
287
  name=ModelProviderName.amazon_bedrock,
205
288
  provider_options={
@@ -207,11 +290,10 @@ built_in_models: List[KilnModel] = [
207
290
  "region_name": "us-west-2", # Llama 3.1 only in west-2
208
291
  },
209
292
  ),
210
- # TODO: enable once tests update to check if model is available
211
- # KilnModelProvider(
212
- # name=ModelProviderName.ollama,
213
- # provider_options={"model": "llama3.1:405b"},
214
- # ),
293
+ KilnModelProvider(
294
+ name=ModelProviderName.ollama,
295
+ provider_options={"model": "llama3.1:405b"},
296
+ ),
215
297
  KilnModelProvider(
216
298
  name=ModelProviderName.openrouter,
217
299
  provider_options={"model": "meta-llama/llama-3.1-405b-instruct"},
@@ -247,11 +329,49 @@ built_in_models: List[KilnModel] = [
247
329
  name=ModelProviderName.openrouter,
248
330
  provider_options={"model": "mistralai/mistral-large"},
249
331
  ),
250
- # TODO: enable once tests update to check if model is available
251
- # KilnModelProvider(
252
- # provider=ModelProviders.ollama,
253
- # provider_options={"model": "mistral-large"},
254
- # ),
332
+ KilnModelProvider(
333
+ name=ModelProviderName.ollama,
334
+ provider_options={"model": "mistral-large"},
335
+ ),
336
+ ],
337
+ ),
338
+ # Llama 3.2 3B
339
+ KilnModel(
340
+ family=ModelFamily.llama,
341
+ name=ModelName.llama_3_2_3b,
342
+ friendly_name="Llama 3.2 3B",
343
+ providers=[
344
+ KilnModelProvider(
345
+ name=ModelProviderName.openrouter,
346
+ supports_structured_output=False,
347
+ provider_options={"model": "meta-llama/llama-3.2-3b-instruct"},
348
+ ),
349
+ ],
350
+ ),
351
+ # Llama 3.2 11B
352
+ KilnModel(
353
+ family=ModelFamily.llama,
354
+ name=ModelName.llama_3_2_11b,
355
+ friendly_name="Llama 3.2 11B",
356
+ providers=[
357
+ KilnModelProvider(
358
+ name=ModelProviderName.openrouter,
359
+ supports_structured_output=False,
360
+ provider_options={"model": "meta-llama/llama-3.2-11b-vision-instruct"},
361
+ ),
362
+ ],
363
+ ),
364
+ # Llama 3.2 90B
365
+ KilnModel(
366
+ family=ModelFamily.llama,
367
+ name=ModelName.llama_3_2_90b,
368
+ friendly_name="Llama 3.2 90B",
369
+ providers=[
370
+ KilnModelProvider(
371
+ name=ModelProviderName.openrouter,
372
+ supports_structured_output=False,
373
+ provider_options={"model": "meta-llama/llama-3.2-90b-vision-instruct"},
374
+ ),
255
375
  ],
256
376
  ),
257
377
  # Phi 3.5
@@ -263,6 +383,7 @@ built_in_models: List[KilnModel] = [
263
383
  providers=[
264
384
  KilnModelProvider(
265
385
  name=ModelProviderName.ollama,
386
+ supports_structured_output=False,
266
387
  provider_options={"model": "phi3.5"},
267
388
  ),
268
389
  KilnModelProvider(
@@ -280,6 +401,7 @@ built_in_models: List[KilnModel] = [
280
401
  providers=[
281
402
  KilnModelProvider(
282
403
  name=ModelProviderName.ollama,
404
+ supports_structured_output=False,
283
405
  provider_options={
284
406
  "model": "gemma2:2b",
285
407
  },
@@ -293,13 +415,12 @@ built_in_models: List[KilnModel] = [
293
415
  friendly_name="Gemma 2 9B",
294
416
  supports_structured_output=False,
295
417
  providers=[
296
- # TODO: enable once tests update to check if model is available
297
- # KilnModelProvider(
298
- # name=ModelProviderName.ollama,
299
- # provider_options={
300
- # "model": "gemma2:9b",
301
- # },
302
- # ),
418
+ KilnModelProvider(
419
+ name=ModelProviderName.ollama,
420
+ provider_options={
421
+ "model": "gemma2:9b",
422
+ },
423
+ ),
303
424
  KilnModelProvider(
304
425
  name=ModelProviderName.openrouter,
305
426
  provider_options={"model": "google/gemma-2-9b-it"},
@@ -313,13 +434,12 @@ built_in_models: List[KilnModel] = [
313
434
  friendly_name="Gemma 2 27B",
314
435
  supports_structured_output=False,
315
436
  providers=[
316
- # TODO: enable once tests update to check if model is available
317
- # KilnModelProvider(
318
- # name=ModelProviderName.ollama,
319
- # provider_options={
320
- # "model": "gemma2:27b",
321
- # },
322
- # ),
437
+ KilnModelProvider(
438
+ name=ModelProviderName.ollama,
439
+ provider_options={
440
+ "model": "gemma2:27b",
441
+ },
442
+ ),
323
443
  KilnModelProvider(
324
444
  name=ModelProviderName.openrouter,
325
445
  provider_options={"model": "google/gemma-2-27b-it"},
@@ -417,7 +537,9 @@ def check_provider_warnings(provider_name: ModelProviderName):
417
537
  raise ValueError(warning_check.message)
418
538
 
419
539
 
420
- def langchain_model_from(name: str, provider_name: str | None = None) -> BaseChatModel:
540
+ async def langchain_model_from(
541
+ name: str, provider_name: str | None = None
542
+ ) -> BaseChatModel:
421
543
  """
422
544
  Creates a LangChain chat model instance for the specified model and provider.
423
545
 
@@ -476,7 +598,23 @@ def langchain_model_from(name: str, provider_name: str | None = None) -> BaseCha
476
598
  **provider.provider_options,
477
599
  )
478
600
  elif provider.name == ModelProviderName.ollama:
479
- return ChatOllama(**provider.provider_options, base_url=ollama_base_url())
601
+ # Ollama model naming is pretty flexible. We try a few versions of the model name
602
+ potential_model_names = []
603
+ if "model" in provider.provider_options:
604
+ potential_model_names.append(provider.provider_options["model"])
605
+ if "model_aliases" in provider.provider_options:
606
+ potential_model_names.extend(provider.provider_options["model_aliases"])
607
+
608
+ # Get the list of models Ollama supports
609
+ ollama_connection = await get_ollama_connection()
610
+ if ollama_connection is None:
611
+ raise ValueError("Failed to connect to Ollama. Ensure Ollama is running.")
612
+
613
+ for model_name in potential_model_names:
614
+ if ollama_model_supported(ollama_connection, model_name):
615
+ return ChatOllama(model=model_name, base_url=ollama_base_url())
616
+
617
+ raise ValueError(f"Model {name} not installed on Ollama")
480
618
  elif provider.name == ModelProviderName.openrouter:
481
619
  api_key = Config.shared().open_router_api_key
482
620
  base_url = getenv("OPENROUTER_BASE_URL") or "https://openrouter.ai/api/v1"
@@ -519,3 +657,67 @@ async def ollama_online() -> bool:
519
657
  except httpx.RequestError:
520
658
  return False
521
659
  return True
660
+
661
+
662
+ class OllamaConnection(BaseModel):
663
+ message: str
664
+ models: List[str]
665
+
666
+
667
+ # Parse the Ollama /api/tags response
668
+ def parse_ollama_tags(tags: Any) -> OllamaConnection | None:
669
+ # Build a list of models we support for Ollama from the built-in model list
670
+ supported_ollama_models = [
671
+ provider.provider_options["model"]
672
+ for model in built_in_models
673
+ for provider in model.providers
674
+ if provider.name == ModelProviderName.ollama
675
+ ]
676
+ # Append model_aliases to supported_ollama_models
677
+ supported_ollama_models.extend(
678
+ [
679
+ alias
680
+ for model in built_in_models
681
+ for provider in model.providers
682
+ for alias in provider.provider_options.get("model_aliases", [])
683
+ ]
684
+ )
685
+
686
+ if "models" in tags:
687
+ models = tags["models"]
688
+ if isinstance(models, list):
689
+ model_names = [model["model"] for model in models]
690
+ print(f"model_names: {model_names}")
691
+ available_supported_models = [
692
+ model
693
+ for model in model_names
694
+ if model in supported_ollama_models
695
+ or model in [f"{m}:latest" for m in supported_ollama_models]
696
+ ]
697
+ if available_supported_models:
698
+ return OllamaConnection(
699
+ message="Ollama connected",
700
+ models=available_supported_models,
701
+ )
702
+
703
+ return OllamaConnection(
704
+ message="Ollama is running, but no supported models are installed. Install one or more supported model, like 'ollama pull phi3.5'.",
705
+ models=[],
706
+ )
707
+
708
+
709
+ async def get_ollama_connection() -> OllamaConnection | None:
710
+ """
711
+ Gets the connection status for Ollama.
712
+ """
713
+ try:
714
+ tags = requests.get(ollama_base_url() + "/api/tags", timeout=5).json()
715
+
716
+ except Exception:
717
+ return None
718
+
719
+ return parse_ollama_tags(tags)
720
+
721
+
722
+ def ollama_model_supported(conn: OllamaConnection, model_name: str) -> bool:
723
+ return model_name in conn.models or f"{model_name}:latest" in conn.models