HowdenLLM 1.5.2__tar.gz → 1.6.0__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.
@@ -9,6 +9,7 @@ import os
9
9
  import json
10
10
  import re
11
11
 
12
+ load_dotenv()
12
13
  class EmptyFileException(Exception):
13
14
  """Exception raised for empty 'filepath' argument"""
14
15
  def __init__(self, fp):
@@ -45,13 +46,13 @@ class LLM:
45
46
  if k not in ("self", "__class__", "__len__")
46
47
  }
47
48
 
48
- load_dotenv()
49
+
49
50
  self.provider_name = provider_and_model.split(":")[0].lower()
50
51
  self.model = provider_and_model.split(":")[1]
51
52
  self.template = template
52
53
  self.name: str = name
53
54
  self.system: str = system
54
- self.provider = ProviderFactory.create(self.provider_name)
55
+ self.client = ProviderFactory.create(self.provider_name)
55
56
  self.use_web_search_tool = use_web_search_tool
56
57
  self.hashed = self.compute_hash(self.input_params)
57
58
 
@@ -87,18 +88,18 @@ class LLM:
87
88
  input_tokens = self._count_tokens(input_text)
88
89
 
89
90
  # --- run model ---
90
- output = self.provider.complete(self.system, prompt, self.model, self.use_web_search_tool)
91
+ output = self.client.complete(self.system, prompt, self.model, self.use_web_search_tool)
91
92
 
92
93
  # --- count output tokens ---
93
- output_tokens = self._count_tokens(output)
94
+ #output_tokens = self._count_tokens(output)
94
95
 
95
96
  # --- update totals ---
96
97
  self.total_input_tokens += input_tokens
97
- self.total_output_tokens += output_tokens
98
+ #self.total_output_tokens += output_tokens
98
99
  self.total_runs += 1
99
100
  print(f"[{self.name or 'LLM'}] "
100
101
  f"Input tokens: {input_tokens}, "
101
- f"Output tokens: {output_tokens}, "
102
+ #f"Output tokens: {output_tokens}, "
102
103
  f"Total_input: {self.total_input_tokens}, "
103
104
  f"Total_output: {self.total_output_tokens}, "
104
105
  f"Total_input_average: {round(self.total_input_tokens / self.total_runs, 2)}, "
@@ -0,0 +1,2 @@
1
+ class KnownProviders:
2
+ Anthropic = 'anthropic'
@@ -0,0 +1,29 @@
1
+ from abc import ABC
2
+ from HowdenLLM.providers.base_provider import BaseProvider
3
+ from anthropic import Anthropic
4
+ import os
5
+ from dotenv import load_dotenv
6
+
7
+ class AnthropicProvider(BaseProvider, ABC):
8
+ provider = "anthropic"
9
+
10
+ def __init__(self):
11
+ load_dotenv()
12
+ self.client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
13
+
14
+ def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
15
+ if model in ["claude-opus-4-6","claude-sonnet-4-6"]:
16
+ message = self.client.messages.create(
17
+ model=model,
18
+ system=system,
19
+ messages=[
20
+ {"role": "user","content": prompt}
21
+ ],
22
+ max_tokens=16000,
23
+ temperature=0.0
24
+
25
+ )
26
+ else:
27
+ raise Exception(f"Unsupported model: {model}")
28
+
29
+ return message.content[0].text
@@ -1,15 +1,23 @@
1
1
  import importlib
2
2
  import inspect
3
3
  import pkgutil
4
+
4
5
  from pathlib import Path
5
6
  from .provider_meta import ProviderMeta
6
- from openai import OpenAI
7
- from dotenv import load_dotenv
8
- import os
7
+
9
8
 
10
9
  class ProviderFactory:
11
10
  """Factory that dynamically loads provider modules and instantiates them."""
12
- openai_client = None
11
+ clients_alive = {}
12
+ providers: dict = None
13
+
14
+ @staticmethod
15
+ def get_providers() -> dict:
16
+ if ProviderFactory.providers is None:
17
+ ProviderFactory._load_all_providers()
18
+ ProviderFactory.providers = ProviderMeta.get_registry()
19
+
20
+ return ProviderFactory.providers
13
21
 
14
22
  @staticmethod
15
23
  def _load_all_providers():
@@ -26,9 +34,9 @@ class ProviderFactory:
26
34
  @staticmethod
27
35
  def create(provider_name: str):
28
36
  """Instantiate a provider by name using the registry."""
29
- ProviderFactory._load_all_providers()
37
+ #ProviderFactory._load_all_providers()
38
+ providers = ProviderFactory.get_providers()
30
39
 
31
- providers = ProviderMeta.get_registry()
32
40
  key = provider_name.lower()
33
41
 
34
42
  if key not in providers:
@@ -37,11 +45,8 @@ class ProviderFactory:
37
45
  f"Available: {list(providers.keys())}"
38
46
  )
39
47
 
40
- if key == "openai" and ProviderFactory.openai_client is None:
41
- load_dotenv()
42
- ProviderFactory.openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
48
+ if key not in ProviderFactory.clients_alive.keys():
49
+ ProviderFactory.clients_alive[key] = providers[key]()
50
+
51
+ return ProviderFactory.clients_alive[key]
43
52
 
44
- if key == "openai":
45
- return providers[key](ProviderFactory.openai_client)
46
- else:
47
- return providers[key]()
@@ -1,13 +1,14 @@
1
1
  from HowdenLLM.providers.base_provider import BaseProvider
2
2
  from dotenv import load_dotenv
3
3
  import os
4
+ from huggingface_hub import InferenceClient
5
+
4
6
 
5
7
 
6
8
  class HuggingFaceProvider(BaseProvider):
7
- provider = "huggingface"
9
+ provider = 'huggingface'
8
10
 
9
11
  def __init__(self):
10
- from huggingface_hub import InferenceClient
11
12
  load_dotenv()
12
13
  self.client = InferenceClient(token=os.getenv("HUGGINGFACE_API_KEY"))
13
14
 
@@ -0,0 +1,40 @@
1
+ from abc import ABC
2
+ from HowdenLLM.providers.base_provider import BaseProvider
3
+ from openai import OpenAI, AsyncOpenAI
4
+ import os
5
+ from dotenv import load_dotenv
6
+ import asyncio
7
+
8
+
9
+ SEMAPHORE = asyncio.Semaphore(3)
10
+
11
+ class OpenAIProvider(BaseProvider, ABC):
12
+ provider = "openai"
13
+
14
+ def __init__(self):
15
+ load_dotenv()
16
+ self.client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))
17
+
18
+ async def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
19
+ async with SEMAPHORE:
20
+ if use_web_search_tool:
21
+ tools = [
22
+ { "type": "web_search" }
23
+ ]
24
+ else:
25
+ tools = None
26
+
27
+ if model in ["gpt-5", "gpt-5-mini", "gpt-5-nano"]:
28
+ response = await self.client.responses.create(
29
+ model=model,
30
+ input=[
31
+ {"role": "system", "content": system},
32
+ {"role": "user", "content": prompt}
33
+ ],
34
+ tools=tools,
35
+ max_output_tokens=16000
36
+ )
37
+ else:
38
+ raise Exception(f"Unsupported model: {model}")
39
+
40
+ return response.output_text
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: HowdenLLM
3
- Version: 1.5.2
3
+ Version: 1.6.0
4
4
  Summary: A simple configuration manager with Pydantic and JSON export.
5
5
  License: MIT
6
6
  Keywords: config,configuration,pydantic,json
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Dist: accelerate (>=1.10.0,<2.0.0)
15
+ Requires-Dist: anthropic (>=0.85.0,<0.86.0)
15
16
  Requires-Dist: langchain (>=1.1.3,<2.0.0)
16
17
  Requires-Dist: langchain-community (>=0.3.27,<0.4.0)
17
18
  Requires-Dist: openai (>=1.99.9,<2.0.0)
@@ -3,7 +3,7 @@ name = "HowdenLLM"
3
3
  description = ""
4
4
  readme = "README.md"
5
5
  requires-python = ">=3.12,<3.14"
6
- dependencies = [ "transformers (>=4.55.0,<5.0.0)", "pydantic (>=2.11.7,<3.0.0)", "python-dotenv (>=1.1.1,<2.0.0)", "langchain-community (>=0.3.27,<0.4.0)", "openai (>=1.99.9,<2.0.0)", "accelerate (>=1.10.0,<2.0.0)", "tiktoken (>=0.12.0,<0.13.0)", "langchain (>=1.1.3,<2.0.0)", "pytest (>=9.0.2,<10.0.0)",]
6
+ dependencies = [ "transformers (>=4.55.0,<5.0.0)", "pydantic (>=2.11.7,<3.0.0)", "python-dotenv (>=1.1.1,<2.0.0)", "langchain-community (>=0.3.27,<0.4.0)", "openai (>=1.99.9,<2.0.0)", "accelerate (>=1.10.0,<2.0.0)", "tiktoken (>=0.12.0,<0.13.0)", "langchain (>=1.1.3,<2.0.0)", "pytest (>=9.0.2,<10.0.0)", "anthropic (>=0.85.0,<0.86.0)",]
7
7
  [[project.authors]]
8
8
  name = "JesperThoftIllemannJ"
9
9
  email = "jesper.jaeger@howdendanmark.dk"
@@ -14,7 +14,7 @@ build-backend = "poetry.core.masonry.api"
14
14
 
15
15
  [tool.poetry]
16
16
  name = "HowdenLLM"
17
- version = "1.5.2"
17
+ version = "1.6.0"
18
18
  description = "A simple configuration manager with Pydantic and JSON export."
19
19
  authors = [ "JesperThoftIllemannJ <jesper.jaeger@howdendanmark.dk>",]
20
20
  readme = "README.md"
@@ -1,33 +0,0 @@
1
- from abc import ABC
2
- from HowdenLLM.providers.base_provider import BaseProvider
3
- from openai import OpenAI
4
-
5
- class OpenAIProvider(BaseProvider, ABC):
6
- provider = "openai"
7
-
8
- def __init__(self,client: OpenAI):
9
- self.client = client
10
-
11
- def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
12
-
13
- if use_web_search_tool:
14
- tools = [
15
- { "type": "web_search" }
16
- ]
17
- else:
18
- tools = None
19
-
20
- if model in ["gpt-5", "gpt-5-mini", "gpt-5-nano"]:
21
- response = self.client.responses.create(
22
- model=model,
23
- input=[
24
- {"role": "system", "content": system},
25
- {"role": "user", "content": prompt}
26
- ],
27
- tools=tools,
28
- max_output_tokens=16000
29
- )
30
- else:
31
- raise Exception(f"Unsupported model: {model}")
32
-
33
- return response.output_text
File without changes