HowdenLLM 1.6.0__tar.gz → 1.6.2__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.
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/manager.py +6 -7
- howdenllm-1.6.2/HowdenLLM/providers/known_providers.py +4 -0
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/providers/provider_anthropic.py +4 -6
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/providers/provider_factory.py +18 -17
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/providers/provider_huggingface.py +4 -5
- howdenllm-1.6.2/HowdenLLM/providers/provider_openai.py +34 -0
- {howdenllm-1.6.0 → howdenllm-1.6.2}/PKG-INFO +1 -1
- {howdenllm-1.6.0 → howdenllm-1.6.2}/pyproject.toml +1 -1
- howdenllm-1.6.0/HowdenLLM/providers/known_providers.py +0 -2
- howdenllm-1.6.0/HowdenLLM/providers/provider_openai.py +0 -40
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/__init__.py +0 -0
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/providers/__init__.py +0 -0
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/providers/base_provider.py +0 -0
- {howdenllm-1.6.0 → howdenllm-1.6.2}/HowdenLLM/providers/provider_meta.py +0 -0
- {howdenllm-1.6.0 → howdenllm-1.6.2}/README.md +0 -0
|
@@ -9,7 +9,6 @@ import os
|
|
|
9
9
|
import json
|
|
10
10
|
import re
|
|
11
11
|
|
|
12
|
-
load_dotenv()
|
|
13
12
|
class EmptyFileException(Exception):
|
|
14
13
|
"""Exception raised for empty 'filepath' argument"""
|
|
15
14
|
def __init__(self, fp):
|
|
@@ -46,13 +45,13 @@ class LLM:
|
|
|
46
45
|
if k not in ("self", "__class__", "__len__")
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
load_dotenv()
|
|
50
49
|
self.provider_name = provider_and_model.split(":")[0].lower()
|
|
51
50
|
self.model = provider_and_model.split(":")[1]
|
|
52
51
|
self.template = template
|
|
53
52
|
self.name: str = name
|
|
54
53
|
self.system: str = system
|
|
55
|
-
self.
|
|
54
|
+
self.provider = ProviderFactory.create(self.provider_name)
|
|
56
55
|
self.use_web_search_tool = use_web_search_tool
|
|
57
56
|
self.hashed = self.compute_hash(self.input_params)
|
|
58
57
|
|
|
@@ -88,18 +87,18 @@ class LLM:
|
|
|
88
87
|
input_tokens = self._count_tokens(input_text)
|
|
89
88
|
|
|
90
89
|
# --- run model ---
|
|
91
|
-
output = self.
|
|
90
|
+
output = self.provider.complete(self.system, prompt, self.model, self.use_web_search_tool)
|
|
92
91
|
|
|
93
92
|
# --- count output tokens ---
|
|
94
|
-
|
|
93
|
+
output_tokens = self._count_tokens(output)
|
|
95
94
|
|
|
96
95
|
# --- update totals ---
|
|
97
96
|
self.total_input_tokens += input_tokens
|
|
98
|
-
|
|
97
|
+
self.total_output_tokens += output_tokens
|
|
99
98
|
self.total_runs += 1
|
|
100
99
|
print(f"[{self.name or 'LLM'}] "
|
|
101
100
|
f"Input tokens: {input_tokens}, "
|
|
102
|
-
|
|
101
|
+
f"Output tokens: {output_tokens}, "
|
|
103
102
|
f"Total_input: {self.total_input_tokens}, "
|
|
104
103
|
f"Total_output: {self.total_output_tokens}, "
|
|
105
104
|
f"Total_input_average: {round(self.total_input_tokens / self.total_runs, 2)}, "
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
2
|
from HowdenLLM.providers.base_provider import BaseProvider
|
|
3
3
|
from anthropic import Anthropic
|
|
4
|
-
import
|
|
5
|
-
from dotenv import load_dotenv
|
|
4
|
+
from .known_providers import KnownProviders
|
|
6
5
|
|
|
7
6
|
class AnthropicProvider(BaseProvider, ABC):
|
|
8
|
-
provider =
|
|
7
|
+
provider = KnownProviders.Anthropic
|
|
9
8
|
|
|
10
|
-
def __init__(self):
|
|
11
|
-
|
|
12
|
-
self.client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
|
|
9
|
+
def __init__(self,client: Anthropic):
|
|
10
|
+
self.client = client
|
|
13
11
|
|
|
14
12
|
def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
|
|
15
13
|
if model in ["claude-opus-4-6","claude-sonnet-4-6"]:
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import importlib
|
|
2
2
|
import inspect
|
|
3
3
|
import pkgutil
|
|
4
|
-
|
|
4
|
+
import os
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from .provider_meta import ProviderMeta
|
|
7
|
-
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
from .known_providers import KnownProviders
|
|
8
9
|
|
|
9
10
|
class ProviderFactory:
|
|
10
11
|
"""Factory that dynamically loads provider modules and instantiates them."""
|
|
11
12
|
clients_alive = {}
|
|
12
|
-
|
|
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
|
+
openai_client = None
|
|
21
14
|
|
|
22
15
|
@staticmethod
|
|
23
16
|
def _load_all_providers():
|
|
@@ -34,9 +27,9 @@ class ProviderFactory:
|
|
|
34
27
|
@staticmethod
|
|
35
28
|
def create(provider_name: str):
|
|
36
29
|
"""Instantiate a provider by name using the registry."""
|
|
37
|
-
|
|
38
|
-
providers = ProviderFactory.get_providers()
|
|
30
|
+
ProviderFactory._load_all_providers()
|
|
39
31
|
|
|
32
|
+
providers = ProviderMeta.get_registry()
|
|
40
33
|
key = provider_name.lower()
|
|
41
34
|
|
|
42
35
|
if key not in providers:
|
|
@@ -46,7 +39,15 @@ class ProviderFactory:
|
|
|
46
39
|
)
|
|
47
40
|
|
|
48
41
|
if key not in ProviderFactory.clients_alive.keys():
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
load_dotenv()
|
|
43
|
+
if key == KnownProviders.OpenAI:
|
|
44
|
+
from openai import OpenAI
|
|
45
|
+
ProviderFactory.clients_alive[key] = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
|
46
|
+
elif key == KnownProviders.HuggingFace:
|
|
47
|
+
from huggingface_hub import InferenceClient
|
|
48
|
+
ProviderFactory.clients_alive[key] = InferenceClient(token=os.getenv("HUGGINGFACE_API_KEY"))
|
|
49
|
+
elif key == KnownProviders.Anthropic:
|
|
50
|
+
from anthropic import Anthropic
|
|
51
|
+
ProviderFactory.clients_alive[key] = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
|
|
52
|
+
|
|
53
|
+
return providers[key](ProviderFactory.clients_alive[key])
|
|
@@ -2,15 +2,14 @@ from HowdenLLM.providers.base_provider import BaseProvider
|
|
|
2
2
|
from dotenv import load_dotenv
|
|
3
3
|
import os
|
|
4
4
|
from huggingface_hub import InferenceClient
|
|
5
|
-
|
|
5
|
+
from .known_providers import KnownProviders
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class HuggingFaceProvider(BaseProvider):
|
|
9
|
-
provider =
|
|
9
|
+
provider = KnownProviders.HuggingFace
|
|
10
10
|
|
|
11
|
-
def __init__(self):
|
|
12
|
-
|
|
13
|
-
self.client = InferenceClient(token=os.getenv("HUGGINGFACE_API_KEY"))
|
|
11
|
+
def __init__(self,client: InferenceClient):
|
|
12
|
+
self.client = client
|
|
14
13
|
|
|
15
14
|
def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
|
|
16
15
|
full_prompt = f"[System]: {system}\n[User]: {prompt}\n"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from HowdenLLM.providers.base_provider import BaseProvider
|
|
3
|
+
from openai import OpenAI
|
|
4
|
+
from .known_providers import KnownProviders
|
|
5
|
+
|
|
6
|
+
class OpenAIProvider(BaseProvider, ABC):
|
|
7
|
+
provider = KnownProviders.OpenAI
|
|
8
|
+
|
|
9
|
+
def __init__(self,client: OpenAI):
|
|
10
|
+
self.client = client
|
|
11
|
+
|
|
12
|
+
def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
|
|
13
|
+
|
|
14
|
+
if use_web_search_tool:
|
|
15
|
+
tools = [
|
|
16
|
+
{ "type": "web_search" }
|
|
17
|
+
]
|
|
18
|
+
else:
|
|
19
|
+
tools = None
|
|
20
|
+
|
|
21
|
+
if model in ["gpt-5", "gpt-5.2", "gpt-5-mini", "gpt-5.4-mini", "gpt-5-nano"]:
|
|
22
|
+
response = self.client.responses.create(
|
|
23
|
+
model=model,
|
|
24
|
+
input=[
|
|
25
|
+
{"role": "system", "content": system},
|
|
26
|
+
{"role": "user", "content": prompt}
|
|
27
|
+
],
|
|
28
|
+
tools=tools,
|
|
29
|
+
max_output_tokens=16000
|
|
30
|
+
)
|
|
31
|
+
else:
|
|
32
|
+
raise Exception(f"Unsupported model: {model}")
|
|
33
|
+
|
|
34
|
+
return response.output_text
|
|
@@ -14,7 +14,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
14
14
|
|
|
15
15
|
[tool.poetry]
|
|
16
16
|
name = "HowdenLLM"
|
|
17
|
-
version = "1.6.
|
|
17
|
+
version = "1.6.2"
|
|
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,40 +0,0 @@
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|