HowdenLLM 1.6.2__tar.gz → 3.0.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.
@@ -35,6 +35,7 @@ class LLM:
35
35
  provider_and_model: str,
36
36
  template: Template,
37
37
  use_web_search_tool: bool,
38
+ upload_attachment: str|None,
38
39
  system: str = None,
39
40
  name: str = None
40
41
  ):
@@ -53,6 +54,7 @@ class LLM:
53
54
  self.system: str = system
54
55
  self.provider = ProviderFactory.create(self.provider_name)
55
56
  self.use_web_search_tool = use_web_search_tool
57
+ self.upload_attachment = upload_attachment
56
58
  self.hashed = self.compute_hash(self.input_params)
57
59
 
58
60
  self.total_input_tokens = 0
@@ -68,7 +70,7 @@ class LLM:
68
70
  except (KeyError, LookupError, AttributeError, ImportError):
69
71
  return len(text.split())
70
72
 
71
- def __call__(self, path_or_content: Path | str) -> str:
73
+ async def __call__(self, path_or_content: Path | str) -> str:
72
74
  """
73
75
  Execute one completion round and return:
74
76
  (output_text, input_token_count, output_token_count)
@@ -87,7 +89,7 @@ class LLM:
87
89
  input_tokens = self._count_tokens(input_text)
88
90
 
89
91
  # --- run model ---
90
- output = self.provider.complete(self.system, prompt, self.model, self.use_web_search_tool)
92
+ output = await self.provider.complete(self.system, prompt, self.model, self.use_web_search_tool,self.upload_attachment)
91
93
 
92
94
  # --- count output tokens ---
93
95
  output_tokens = self._count_tokens(output)
@@ -0,0 +1,7 @@
1
+ from abc import ABC, abstractmethod
2
+ from .provider_meta import ProviderMeta
3
+
4
+ class BaseProvider(ABC, metaclass=ProviderMeta):
5
+ @abstractmethod
6
+ async def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool, upload_attachment: str|None) -> str:
7
+ pass
@@ -0,0 +1,42 @@
1
+ from abc import ABC
2
+ from HowdenLLM.providers.base_provider import BaseProvider
3
+ from anthropic import AsyncAnthropic
4
+ from .known_providers import KnownProviders
5
+
6
+ class AnthropicProvider(BaseProvider, ABC):
7
+ provider = KnownProviders.Anthropic
8
+
9
+ def __init__(self,client: AsyncAnthropic):
10
+ self.client = client
11
+
12
+ async def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool, upload_attachment: str) -> str:
13
+
14
+ if use_web_search_tool:
15
+ tools=[{"type": "web_search_20260209","name": "web_search","max_uses": 5}]
16
+ else:
17
+ tools = []
18
+
19
+ if model in ["claude-opus-4-6","claude-opus-4-5-20251101","claude-sonnet-4-6","claude-sonnet-4-5-20250929"]:
20
+ message = await self.client.messages.create(
21
+ model=model,
22
+ system=system,
23
+ messages=[
24
+ {"role": "user","content": prompt}
25
+ ],
26
+ max_tokens=16000,
27
+ temperature=0.0,
28
+ tools=tools,
29
+ stream=False,
30
+ # thinking={"type": "adaptive"},
31
+ # output_config={"effort": "high"}, # or max, medium, low
32
+ )
33
+ else:
34
+ raise Exception(f"Unsupported model: {model}")
35
+
36
+ # print(f"Content: {message}")
37
+ clean_response = "\n".join(
38
+ block.text for block in message.content if block.type == "text"
39
+ )
40
+
41
+
42
+ return clean_response
@@ -41,13 +41,13 @@ class ProviderFactory:
41
41
  if key not in ProviderFactory.clients_alive.keys():
42
42
  load_dotenv()
43
43
  if key == KnownProviders.OpenAI:
44
- from openai import OpenAI
45
- ProviderFactory.clients_alive[key] = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
44
+ from openai import AsyncOpenAI
45
+ ProviderFactory.clients_alive[key] = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))
46
46
  elif key == KnownProviders.HuggingFace:
47
47
  from huggingface_hub import InferenceClient
48
48
  ProviderFactory.clients_alive[key] = InferenceClient(token=os.getenv("HUGGINGFACE_API_KEY"))
49
49
  elif key == KnownProviders.Anthropic:
50
- from anthropic import Anthropic
51
- ProviderFactory.clients_alive[key] = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
50
+ from anthropic import AsyncAnthropic
51
+ ProviderFactory.clients_alive[key] = AsyncAnthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
52
52
 
53
53
  return providers[key](ProviderFactory.clients_alive[key])
@@ -3,6 +3,7 @@ from dotenv import load_dotenv
3
3
  import os
4
4
  from huggingface_hub import InferenceClient
5
5
  from .known_providers import KnownProviders
6
+ import asyncio
6
7
 
7
8
 
8
9
  class HuggingFaceProvider(BaseProvider):
@@ -11,9 +12,10 @@ class HuggingFaceProvider(BaseProvider):
11
12
  def __init__(self,client: InferenceClient):
12
13
  self.client = client
13
14
 
14
- def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
15
+ async def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
15
16
  full_prompt = f"[System]: {system}\n[User]: {prompt}\n"
16
- response = self.client.text_generation(
17
+ response = await asyncio.to_thread(
18
+ self.client.text_generation,
17
19
  model=model,
18
20
  prompt=full_prompt,
19
21
  max_new_tokens=500,
@@ -0,0 +1,53 @@
1
+ from abc import ABC
2
+
3
+ from sympy.codegen.ast import continue_
4
+
5
+ from HowdenLLM.providers.base_provider import BaseProvider
6
+ from openai import AsyncOpenAI
7
+ from .known_providers import KnownProviders
8
+
9
+ class OpenAIProvider(BaseProvider, ABC):
10
+ provider = KnownProviders.OpenAI
11
+
12
+ def __init__(self,client: AsyncOpenAI):
13
+ self.client = client
14
+
15
+ async def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool, upload_attachment: str|None) -> str:
16
+
17
+ if use_web_search_tool:
18
+ tools = [
19
+ { "type": "web_search" }
20
+ ]
21
+ else:
22
+ tools = None
23
+
24
+ if upload_attachment:
25
+ with open(upload_attachment, "rb") as f:
26
+ uploaded = self.client.files.create(
27
+ file=f,
28
+ purpose="user_data",
29
+ )
30
+ input=[
31
+ {"role": "system", "content": system},
32
+ {"role": "user", "content": [
33
+ {"type": "input_text", "text": prompt},
34
+ {"type": "input_file", "file_id": uploaded.id},
35
+ ]}
36
+ ]
37
+ else:
38
+ input=[
39
+ {"role": "system", "content": system},
40
+ {"role": "user", "content": prompt}
41
+ ]
42
+
43
+ if model in ["gpt-5", "gpt-5.2", "gpt-5-mini", "gpt-5.4-mini", "gpt-5-nano"]:
44
+ response = await self.client.responses.create(
45
+ model=model,
46
+ input=input,
47
+ tools=tools,
48
+ max_output_tokens=16000
49
+ )
50
+ else:
51
+ raise Exception(f"Unsupported model: {model}")
52
+
53
+ return response.output_text
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: HowdenLLM
3
- Version: 1.6.2
3
+ Version: 3.0.0
4
4
  Summary: A simple configuration manager with Pydantic and JSON export.
5
5
  License: MIT
6
6
  Keywords: config,configuration,pydantic,json
@@ -1,5 +1,6 @@
1
1
  [project]
2
2
  name = "HowdenLLM"
3
+ version = "3.0.0"
3
4
  description = ""
4
5
  readme = "README.md"
5
6
  requires-python = ">=3.12,<3.14"
@@ -14,7 +15,7 @@ build-backend = "poetry.core.masonry.api"
14
15
 
15
16
  [tool.poetry]
16
17
  name = "HowdenLLM"
17
- version = "1.6.2"
18
+ version = "1.7.0"
18
19
  description = "A simple configuration manager with Pydantic and JSON export."
19
20
  authors = [ "JesperThoftIllemannJ <jesper.jaeger@howdendanmark.dk>",]
20
21
  readme = "README.md"
@@ -1,7 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from .provider_meta import ProviderMeta
3
-
4
- class BaseProvider(ABC, metaclass=ProviderMeta):
5
- @abstractmethod
6
- def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
7
- pass
@@ -1,27 +0,0 @@
1
- from abc import ABC
2
- from HowdenLLM.providers.base_provider import BaseProvider
3
- from anthropic import Anthropic
4
- from .known_providers import KnownProviders
5
-
6
- class AnthropicProvider(BaseProvider, ABC):
7
- provider = KnownProviders.Anthropic
8
-
9
- def __init__(self,client: Anthropic):
10
- self.client = client
11
-
12
- def complete(self, system: str, prompt: str, model: str, use_web_search_tool: bool) -> str:
13
- if model in ["claude-opus-4-6","claude-sonnet-4-6"]:
14
- message = self.client.messages.create(
15
- model=model,
16
- system=system,
17
- messages=[
18
- {"role": "user","content": prompt}
19
- ],
20
- max_tokens=16000,
21
- temperature=0.0
22
-
23
- )
24
- else:
25
- raise Exception(f"Unsupported model: {model}")
26
-
27
- return message.content[0].text
@@ -1,34 +0,0 @@
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
File without changes