astoncli 0.1.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.
aston/llm.py ADDED
@@ -0,0 +1,182 @@
1
+ import os
2
+ from abc import ABC, abstractmethod
3
+ from aston.exceptions import LLMError
4
+
5
+ class BaseLLMProvider(ABC):
6
+ @abstractmethod
7
+ def generate(self, prompt: str, model: str, temperature: float = 0.0, system_prompt: str = None, response_format=None) -> str | object:
8
+ pass
9
+
10
+
11
+ class MistralProvider(BaseLLMProvider):
12
+ def generate(self, prompt: str, model: str, temperature: float = 0.0, system_prompt: str = None, response_format=None) -> str:
13
+ from mistralai import Mistral
14
+ api_key = os.getenv("MISTRAL_API_KEY")
15
+ if not api_key:
16
+ raise LLMError(
17
+ "MISTRAL_API_KEY environment variable is not set.",
18
+ details="Provider: mistral | Model: {} | Action: Export your Mistral API key using 'export MISTRAL_API_KEY=\"...\"'".format(model)
19
+ )
20
+
21
+ try:
22
+ client = Mistral(api_key=api_key)
23
+ messages = []
24
+ if system_prompt:
25
+ messages.append({"role": "system", "content": system_prompt})
26
+ messages.append({"role": "user", "content": prompt})
27
+
28
+ kwargs = {
29
+ "model": model,
30
+ "messages": messages,
31
+ "temperature": temperature
32
+ }
33
+ if response_format:
34
+ kwargs["response_format"] = {"type": "json_object"}
35
+
36
+ response = client.chat.complete(**kwargs)
37
+ return response.choices[0].message.content.strip()
38
+ except Exception as exc:
39
+ raise LLMError(
40
+ message="Mistral API generation failed.",
41
+ details=f"Provider: mistral | Model: {model} | Underlying Error: {type(exc).__name__}: {exc}"
42
+ )
43
+
44
+
45
+ class OpenAIProvider(BaseLLMProvider):
46
+ def generate(self, prompt: str, model: str, temperature: float = 0.0, system_prompt: str = None, response_format=None):
47
+ from openai import OpenAI
48
+ api_key = os.getenv("OPENAI_API_KEY")
49
+ if not api_key:
50
+ raise LLMError(
51
+ message="OPENAI_API_KEY environment variable is not set.",
52
+ details="Provider: openai | Model: {} | Action: Export your OpenAI API key using 'export OPENAI_API_KEY=\"...\"'".format(model)
53
+ )
54
+
55
+ try:
56
+ client = OpenAI(api_key=api_key)
57
+ messages = []
58
+ if system_prompt:
59
+ messages.append({"role": "system", "content": system_prompt})
60
+ messages.append({"role": "user", "content": prompt})
61
+
62
+ if response_format:
63
+ completion = client.beta.chat.completions.parse(
64
+ model=model,
65
+ messages=messages,
66
+ temperature=temperature,
67
+ response_format=response_format,
68
+ )
69
+ return completion.choices[0].message.parsed
70
+ else:
71
+ completion = client.chat.completions.create(
72
+ model=model,
73
+ messages=messages,
74
+ temperature=temperature
75
+ )
76
+ return completion.choices[0].message.content.strip()
77
+ except Exception as exc:
78
+ raise LLMError(
79
+ message="OpenAI API generation failed.",
80
+ details=f"Provider: openai | Model: {model} | Underlying Error: {type(exc).__name__}: {exc}"
81
+ )
82
+
83
+
84
+ class AnthropicProvider(BaseLLMProvider):
85
+ def generate(self, prompt: str, model: str, temperature: float = 0.0, system_prompt: str = None, response_format=None) -> str:
86
+ import anthropic
87
+ api_key = os.getenv("ANTHROPIC_API_KEY")
88
+ if not api_key:
89
+ raise LLMError(
90
+ message="ANTHROPIC_API_KEY environment variable is not set.",
91
+ details="Provider: anthropic | Model: {} | Action: Export your Anthropic API key using 'export ANTHROPIC_API_KEY=\"...\"'".format(model)
92
+ )
93
+
94
+ try:
95
+ client = anthropic.Anthropic(api_key=api_key)
96
+ kwargs = {
97
+ "model": model,
98
+ "max_tokens": 4096,
99
+ "temperature": temperature,
100
+ "messages": [{"role": "user", "content": prompt}]
101
+ }
102
+ if system_prompt:
103
+ sys_content = system_prompt
104
+ if response_format:
105
+ sys_content += "\nProvide your response strictly as a valid JSON object matching the requested schema."
106
+ kwargs["system"] = sys_content
107
+
108
+ response = client.messages.create(**kwargs)
109
+ return response.content[0].text.strip()
110
+ except Exception as exc:
111
+ raise LLMError(
112
+ message="Anthropic API generation failed.",
113
+ details=f"Provider: anthropic | Model: {model} | Underlying Error: {type(exc).__name__}: {exc}"
114
+ )
115
+
116
+
117
+ class GoogleProvider(BaseLLMProvider):
118
+ def generate(self, prompt: str, model: str, temperature: float = 0.0, system_prompt: str = None, response_format=None):
119
+ from google import genai
120
+ from google.genai import types
121
+
122
+ if not (os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")):
123
+ raise LLMError(
124
+ message="GEMINI_API_KEY (or GOOGLE_API_KEY) environment variable is not set.",
125
+ details="Provider: google | Model: {} | Action: Export your Gemini API key using 'export GEMINI_API_KEY=\"...\"'".format(model)
126
+ )
127
+
128
+ try:
129
+ client = genai.Client()
130
+ config_kwargs = {"temperature": temperature}
131
+ if system_prompt:
132
+ config_kwargs["system_instruction"] = system_prompt
133
+
134
+ if response_format:
135
+ config_kwargs["response_mime_type"] = "application/json"
136
+ config_kwargs["response_schema"] = response_format
137
+
138
+ response = client.models.generate_content(
139
+ model=model,
140
+ contents=prompt,
141
+ config=types.GenerateContentConfig(**config_kwargs)
142
+ )
143
+ return response.text.strip()
144
+ except Exception as exc:
145
+ raise LLMError(
146
+ message="Google Gemini API generation failed.",
147
+ details=f"Provider: google | Model: {model} | Underlying Error: {type(exc).__name__}: {exc}"
148
+ )
149
+
150
+
151
+ _PROVIDERS = {
152
+ "mistral": MistralProvider,
153
+ "openai": OpenAIProvider,
154
+ "anthropic": AnthropicProvider,
155
+ "google": GoogleProvider,
156
+ }
157
+
158
+
159
+ def call_llm(
160
+ prompt: str,
161
+ provider: str = "mistral",
162
+ model: str = "codestral-latest",
163
+ temperature: float = 0.0,
164
+ system_prompt: str = None,
165
+ response_format=None
166
+ ) -> str | object:
167
+ provider_lower = provider.lower()
168
+ if provider_lower not in _PROVIDERS:
169
+ supported = list(_PROVIDERS.keys())
170
+ raise LLMError(
171
+ message=f"Unsupported LLM provider: '{provider}'",
172
+ details=f"Requested Provider: {provider} | Supported Providers: {supported}"
173
+ )
174
+
175
+ provider_instance = _PROVIDERS[provider_lower]()
176
+ return provider_instance.generate(
177
+ prompt=prompt,
178
+ model=model,
179
+ temperature=temperature,
180
+ system_prompt=system_prompt,
181
+ response_format=response_format
182
+ )