askai-python 0.1.6__py3-none-any.whl → 0.2.1__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.
- ask_ai/base.py +81 -8
- ask_ai/exceptions.py +18 -3
- ask_ai/utils.py +43 -0
- askai_python-0.2.1.dist-info/METADATA +137 -0
- {askai_python-0.1.6.dist-info → askai_python-0.2.1.dist-info}/RECORD +8 -7
- {askai_python-0.1.6.dist-info → askai_python-0.2.1.dist-info}/WHEEL +1 -1
- askai_python-0.1.6.dist-info/METADATA +0 -135
- {askai_python-0.1.6.dist-info → askai_python-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {askai_python-0.1.6.dist-info → askai_python-0.2.1.dist-info}/top_level.txt +0 -0
ask_ai/base.py
CHANGED
|
@@ -9,9 +9,30 @@ class Response:
|
|
|
9
9
|
"""
|
|
10
10
|
Unified response object for all easyai requests.
|
|
11
11
|
"""
|
|
12
|
-
def __init__(self, text: str = "", media: Union[ImageObject, AudioObject, None] = None):
|
|
13
|
-
self.
|
|
12
|
+
def __init__(self, text: str = "", media: Union[ImageObject, AudioObject, None] = None, **kwargs):
|
|
13
|
+
self._raw_text = text
|
|
14
14
|
self.media = media
|
|
15
|
+
self._kwargs = kwargs
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def text(self) -> str:
|
|
19
|
+
text = self._raw_text
|
|
20
|
+
if self._kwargs.get('strip'):
|
|
21
|
+
from .utils import strip_tags
|
|
22
|
+
text = strip_tags(text)
|
|
23
|
+
if self._kwargs.get('clean'):
|
|
24
|
+
from .utils import clean_markdown
|
|
25
|
+
text = clean_markdown(text)
|
|
26
|
+
if self._kwargs.get('code'):
|
|
27
|
+
from .utils import extract_code
|
|
28
|
+
text = extract_code(text)
|
|
29
|
+
return text
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def json(self) -> Union[Dict[str, Any], list]:
|
|
33
|
+
"""Returns parsed JSON. If 'json=True' was passed, this is highly reliable."""
|
|
34
|
+
from .utils import parse_json
|
|
35
|
+
return parse_json(self._raw_text)
|
|
15
36
|
|
|
16
37
|
def __str__(self):
|
|
17
38
|
return self.text
|
|
@@ -82,18 +103,70 @@ class BaseProvider:
|
|
|
82
103
|
# 3. Check for specific output_type override (e.g. user forced image)
|
|
83
104
|
output_type = kwargs.get('output_type')
|
|
84
105
|
|
|
85
|
-
# 4.
|
|
86
|
-
|
|
106
|
+
# 4. Extract timeout and retry safely
|
|
107
|
+
timeout_val = kwargs.get('timeout', final_config.extra.get('timeout', 30))
|
|
108
|
+
retry_val = kwargs.get('retry', final_config.extra.get('retry', 0))
|
|
109
|
+
|
|
110
|
+
timeout = float(timeout_val) if timeout_val is not None else 30.0
|
|
111
|
+
retry = int(retry_val) if retry_val is not None else 0
|
|
112
|
+
|
|
113
|
+
final_config.extra['timeout'] = timeout # Pass down to providers
|
|
114
|
+
|
|
115
|
+
# 5. Execute with Retry Logic
|
|
116
|
+
attempts = 0
|
|
117
|
+
last_error = None
|
|
118
|
+
while attempts <= retry:
|
|
119
|
+
try:
|
|
120
|
+
response = self._send_request(messages, final_config, output_type)
|
|
121
|
+
# Attach parsing arguments to the response object dynamically
|
|
122
|
+
response._kwargs.update(kwargs)
|
|
123
|
+
return response
|
|
124
|
+
except Exception as e:
|
|
125
|
+
last_error = e
|
|
126
|
+
if self._is_retryable_error(e) and attempts < retry:
|
|
127
|
+
attempts += 1
|
|
128
|
+
delay = 2 ** attempts # 2s, 4s, 8s...
|
|
129
|
+
print(f"[ask-ai] Transient error ({e.__class__.__name__}). Retrying in {delay}s... ({attempts}/{retry})")
|
|
130
|
+
import time
|
|
131
|
+
time.sleep(delay)
|
|
132
|
+
else:
|
|
133
|
+
raise e
|
|
134
|
+
|
|
135
|
+
# Fallback if loop exits without returning
|
|
136
|
+
from .exceptions import AskAINetworkError
|
|
137
|
+
raise AskAINetworkError(f"Request failed after {retry} retries. Last error: {last_error}")
|
|
138
|
+
|
|
139
|
+
def _is_retryable_error(self, e: Exception) -> bool:
|
|
140
|
+
"""Dynamically detect rate limits and network timeouts across different provider SDKs."""
|
|
141
|
+
err_str = str(e).lower()
|
|
142
|
+
err_cls = e.__class__.__name__.lower()
|
|
143
|
+
|
|
144
|
+
# Explicit ask-ai errors
|
|
145
|
+
from .exceptions import AskAINetworkError, AskAIRateLimitError
|
|
146
|
+
if isinstance(e, (AskAINetworkError, AskAIRateLimitError)): return True
|
|
147
|
+
|
|
148
|
+
# Heuristics for underlying provider SDKs (OpenAI, Anthropic, etc.)
|
|
149
|
+
if "ratelimit" in err_cls or "rate_limit" in err_cls or "429" in err_str: return True
|
|
150
|
+
if "timeout" in err_cls or "timeout" in err_str: return True
|
|
151
|
+
if "connection" in err_cls or "connecterror" in err_cls: return True
|
|
152
|
+
|
|
153
|
+
return False
|
|
87
154
|
|
|
88
155
|
def _merge_configs(self, global_conf: AdvancedConfig, req_conf: AdvancedConfig) -> AdvancedConfig:
|
|
89
156
|
return global_conf.merge(req_conf) # We need to implement merge logic in AdvancedConfig properly or here
|
|
90
157
|
|
|
91
158
|
def _prepare_messages(self, query: str, config: AdvancedConfig) -> List[Dict[str, str]]:
|
|
92
159
|
messages = []
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
160
|
+
|
|
161
|
+
system_msg = config.system_message or self.persona or ""
|
|
162
|
+
|
|
163
|
+
# Smart capability: if json=True, enforce JSON system prompt
|
|
164
|
+
if config.extra.get('json'):
|
|
165
|
+
json_instruction = "You must respond with valid JSON only, without any markdown formatting or tags."
|
|
166
|
+
system_msg = f"{system_msg}\n{json_instruction}".strip()
|
|
167
|
+
|
|
168
|
+
if system_msg:
|
|
169
|
+
messages.append({"role": "system", "content": system_msg})
|
|
97
170
|
|
|
98
171
|
messages.append({"role": "user", "content": query})
|
|
99
172
|
return messages
|
ask_ai/exceptions.py
CHANGED
|
@@ -2,14 +2,29 @@ class AskAIError(Exception):
|
|
|
2
2
|
"""Base exception for all ask-ai errors."""
|
|
3
3
|
pass
|
|
4
4
|
|
|
5
|
-
class
|
|
6
|
-
"""Raised when API
|
|
5
|
+
class AskAIConfigError(AskAIError):
|
|
6
|
+
"""Raised when configuration (like API keys) is missing or invalid."""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class AskAINetworkError(AskAIError):
|
|
10
|
+
"""Raised when a network connection fails (timeout, disconnect)."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class AskAIRateLimitError(AskAIError):
|
|
14
|
+
"""Raised when the provider rate limits the request (429)."""
|
|
7
15
|
pass
|
|
8
16
|
|
|
9
17
|
class ProviderError(AskAIError):
|
|
10
|
-
"""Raised when the provider API fails (e.g. 500 error)."""
|
|
18
|
+
"""Raised when the provider API fails (e.g. 500 error or bad request)."""
|
|
11
19
|
pass
|
|
12
20
|
|
|
13
21
|
class MediaTypeNotSupportedError(AskAIError):
|
|
14
22
|
"""Raised when a provider can't handle the requested media type."""
|
|
15
23
|
pass
|
|
24
|
+
|
|
25
|
+
class AskAIParsingError(AskAIError):
|
|
26
|
+
"""Raised when expect_json is True but the response cannot be parsed."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
# Alias for backwards compatibility internally
|
|
30
|
+
APIKeyError = AskAIConfigError
|
ask_ai/utils.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
from typing import Dict, Any, Union
|
|
4
|
+
|
|
5
|
+
def clean_markdown(text: str) -> str:
|
|
6
|
+
"""Removes markdown code blocks like ```json ... ```"""
|
|
7
|
+
text = text.strip()
|
|
8
|
+
if text.startswith("```"):
|
|
9
|
+
# find the first newline
|
|
10
|
+
first_newline = text.find("\n")
|
|
11
|
+
if first_newline != -1:
|
|
12
|
+
text = text[first_newline+1:]
|
|
13
|
+
if text.endswith("```"):
|
|
14
|
+
text = text[:-3]
|
|
15
|
+
return text.strip()
|
|
16
|
+
|
|
17
|
+
def extract_code(text: str) -> str:
|
|
18
|
+
"""Extracts code blocks from text, ignoring conversational filler."""
|
|
19
|
+
text = str(text)
|
|
20
|
+
matches = re.findall(r'```(?:[\w]*\n)?(.*?)```', text, re.DOTALL)
|
|
21
|
+
if matches:
|
|
22
|
+
return "\n\n".join(m.strip() for m in matches)
|
|
23
|
+
return text
|
|
24
|
+
|
|
25
|
+
def strip_tags(text: str) -> str:
|
|
26
|
+
"""Removes XML/HTML tags, including <think>...</think> blocks."""
|
|
27
|
+
# First remove <think>...</think> entirely if we don't want the thought process
|
|
28
|
+
text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
|
|
29
|
+
# Remove any other HTML tags
|
|
30
|
+
text = re.sub(r'<[^>]+>', '', text)
|
|
31
|
+
return text.strip()
|
|
32
|
+
|
|
33
|
+
def parse_json(text: str) -> Union[Dict[str, Any], list]:
|
|
34
|
+
"""Tries to parse JSON from text, employing cleaning if necessary."""
|
|
35
|
+
try:
|
|
36
|
+
return json.loads(text)
|
|
37
|
+
except json.JSONDecodeError:
|
|
38
|
+
cleaned = clean_markdown(text)
|
|
39
|
+
try:
|
|
40
|
+
return json.loads(cleaned)
|
|
41
|
+
except json.JSONDecodeError as e:
|
|
42
|
+
from .exceptions import AskAIParsingError
|
|
43
|
+
raise AskAIParsingError(f"Failed to parse JSON: {e}\nContent snippet: {text[:100]}...")
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: askai-python
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more.
|
|
5
|
+
Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://hosseinghorbani0.ir/
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/Hosseinghorbani0/ask-ai/issues
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: openai>=1.0.0
|
|
13
|
+
Requires-Dist: groq
|
|
14
|
+
Requires-Dist: google-generativeai
|
|
15
|
+
Requires-Dist: anthropic
|
|
16
|
+
Requires-Dist: requests
|
|
17
|
+
Requires-Dist: Pillow
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# askai-python 🚀
|
|
21
|
+
|
|
22
|
+
**A minimal Python SDK to switch between LLM providers in one line.**
|
|
23
|
+
No frameworks. No servers. No overengineering.
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/askai-python/)
|
|
26
|
+
[](https://opensource.org/licenses/MIT)
|
|
27
|
+
[](https://github.com/Hosseinghorbani0/ask-ai)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ⚡ Quick Start (5 seconds)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install askai-python
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from ask_ai import OpenAI, Groq
|
|
39
|
+
|
|
40
|
+
# Auto-detects OPENAI_API_KEY from environment
|
|
41
|
+
print(OpenAI().ask("Explain black holes like I'm 5").text)
|
|
42
|
+
|
|
43
|
+
# Switch provider instantly
|
|
44
|
+
print(Groq().ask("Explain black holes like I'm 5").text)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🧐 Why ask-ai?
|
|
50
|
+
|
|
51
|
+
- **One function**: Just call `.ask()`
|
|
52
|
+
- **Multiple providers**: OpenAI, Anthropic, Google Gemini, Groq, Azure, OpenRouter
|
|
53
|
+
- **Zero config**: Keys are pulled from the environment automatically
|
|
54
|
+
- **SDK-first, not a framework**: It stays out of your way.
|
|
55
|
+
|
|
56
|
+
## 🚫 What this project is NOT
|
|
57
|
+
|
|
58
|
+
> ❌ Not an AI framework
|
|
59
|
+
> ❌ Not an API gateway
|
|
60
|
+
> ❌ Not an agent memory system
|
|
61
|
+
|
|
62
|
+
It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 🛠️ Advanced Usage
|
|
67
|
+
|
|
68
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
69
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from ask_ai import OpenAI
|
|
73
|
+
ai = OpenAI()
|
|
74
|
+
|
|
75
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
76
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
77
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
78
|
+
|
|
79
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
80
|
+
# Great for automation pipelines
|
|
81
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
82
|
+
|
|
83
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
84
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
85
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
86
|
+
|
|
87
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
88
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
89
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
90
|
+
print(data_dict['name'])
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
94
|
+
|
|
95
|
+
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from ask_ai import OpenAI
|
|
99
|
+
|
|
100
|
+
ai = OpenAI()
|
|
101
|
+
|
|
102
|
+
# Automatically retries up to 3 times on transient errors, with a 15-second timeout
|
|
103
|
+
response = ai.ask(
|
|
104
|
+
"Write a complex python script",
|
|
105
|
+
retry=3,
|
|
106
|
+
timeout=15
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 🔌 Supported Providers
|
|
111
|
+
|
|
112
|
+
| Provider | Class | Capabilities |
|
|
113
|
+
|----------|-------|-------------|
|
|
114
|
+
| **OpenAI** | `OpenAI` | Text, Images (DALL-E), Vision |
|
|
115
|
+
| **Anthropic** | `Anthropic` | Text, Vision (Claude 3.5) |
|
|
116
|
+
| **Google** | `Google` | Text, Images, Video, Audio |
|
|
117
|
+
| **Groq** | `Groq` | Ultra-fast Llama 3 & Mixtral |
|
|
118
|
+
| **Azure** | `Azure` | Enterprise-grade OpenAI |
|
|
119
|
+
| **OpenRouter**| `OpenRouter`| 100+ community models |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 🗺️ Roadmap
|
|
124
|
+
|
|
125
|
+
- [x] Baseline multiple providers
|
|
126
|
+
- [x] Automated Retry & Timeout controls
|
|
127
|
+
- [ ] Async API (`await ask_async`)
|
|
128
|
+
- [ ] Provider Fallback (`fallback=[Groq()]`)
|
|
129
|
+
- [ ] Structured Outputs (Pydantic Support)
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🔗 Important Links
|
|
134
|
+
|
|
135
|
+
- **GitHub Repository**: [Hosseinghorbani0/ask-ai](https://github.com/Hosseinghorbani0/ask-ai) (Star us! ⭐)
|
|
136
|
+
- **Official Website**: [hosseinghorbani0.ir](https://hosseinghorbani0.ir/)
|
|
137
|
+
- **Bug Tracker**: [Report an Issue](https://github.com/Hosseinghorbani0/ask-ai/issues)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
ask_ai/__init__.py,sha256=i08NdBtFsjV3vT9ZqhEsroXd7vvZrWDojsOW-79iTV4,439
|
|
2
2
|
ask_ai/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
|
|
3
|
-
ask_ai/base.py,sha256=
|
|
3
|
+
ask_ai/base.py,sha256=pMNc6YhHiWasi1IH6Qe6xdtNwoxzQu68JPW5KBrZCsE,8543
|
|
4
4
|
ask_ai/cli.py,sha256=8oeRLsT2lrcuG0vDq_a4CVqvzzmwDG3HxHW4BYg3IxM,2708
|
|
5
5
|
ask_ai/config.py,sha256=oP5RjdbbdDve8G-FlcbNaMXPorBw96GxhCbfFWnfbIw,1934
|
|
6
|
-
ask_ai/exceptions.py,sha256=
|
|
6
|
+
ask_ai/exceptions.py,sha256=jUP1tJJvvx8ymFXU19Em3JBbTPixEnO_uj6ZC5zDyEM,893
|
|
7
7
|
ask_ai/media.py,sha256=cxPZ0XoY0iU3Y8bY-YV3MGPlNFOJ_FjjwMATgo-jcFw,1821
|
|
8
|
+
ask_ai/utils.py,sha256=cRokmKtoNOgQsGrKTdKBjv9vdG3sXpiA6W9HtcjNIho,1582
|
|
8
9
|
ask_ai/providers/__init__.py,sha256=tVSW-FfSMPzCYK_TZy93W5ckdVe2PBPyzogpKXH_jhg,341
|
|
9
10
|
ask_ai/providers/anthropic_provider.py,sha256=NL_6E7eNTYAoYpym4ewD8Vj8tF_ewuFjFkRR0Jj0L-U,3670
|
|
10
11
|
ask_ai/providers/azure_provider.py,sha256=bY8MlHIjiH_H00Q78_2ooCgN1OPeM3ajpvJ2AkxNsTI,2086
|
|
@@ -12,8 +13,8 @@ ask_ai/providers/google_provider.py,sha256=DsIgu_cBC7ta0lKclAlge1PYfcp3zXKPLjWSe
|
|
|
12
13
|
ask_ai/providers/groq_provider.py,sha256=iU7BTpvmBniE78VdTQETrCXrJzdXTpWH2oN9_sUgnFk,3268
|
|
13
14
|
ask_ai/providers/openai_provider.py,sha256=b6grPbsUIY19HkABDrUIAZAIcJCKQup5tooU2LFlkvo,4793
|
|
14
15
|
ask_ai/providers/openrouter_provider.py,sha256=LCGK-vNxy_maY2Fle6xh_i8MVm7a_bhjCaF7Uj0H3AM,1986
|
|
15
|
-
askai_python-0.1.
|
|
16
|
-
askai_python-0.1.
|
|
17
|
-
askai_python-0.1.
|
|
18
|
-
askai_python-0.1.
|
|
19
|
-
askai_python-0.1.
|
|
16
|
+
askai_python-0.2.1.dist-info/licenses/LICENSE,sha256=SUSbAUsh_Uqw7NDEKwAGRVPS7sojihBHIBGBx6WZxvg,1073
|
|
17
|
+
askai_python-0.2.1.dist-info/METADATA,sha256=sDAgOTv2w_wTOBcJrOp5-c8io7eMgHi13kjcv2h_myE,4218
|
|
18
|
+
askai_python-0.2.1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
19
|
+
askai_python-0.2.1.dist-info/top_level.txt,sha256=fW2GapXaz6f-2UAuzVVPUPNfZUK_ZS1dpLyCllcE03s,7
|
|
20
|
+
askai_python-0.2.1.dist-info/RECORD,,
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: askai-python
|
|
3
|
-
Version: 0.1.6
|
|
4
|
-
Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more.
|
|
5
|
-
Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://hosseinghorbani0.ir/
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/Hosseinghorbani0/ask-ai/issues
|
|
9
|
-
Requires-Python: >=3.8
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: openai>=1.0.0
|
|
13
|
-
Requires-Dist: groq
|
|
14
|
-
Requires-Dist: google-generativeai
|
|
15
|
-
Requires-Dist: anthropic
|
|
16
|
-
Requires-Dist: requests
|
|
17
|
-
Requires-Dist: Pillow
|
|
18
|
-
Dynamic: license-file
|
|
19
|
-
|
|
20
|
-
# askai-python 🚀
|
|
21
|
-
|
|
22
|
-
**AI Made Stupid Simple.** One unified Python client for OpenAI, Claude, Gemini, Groq, and Azure.
|
|
23
|
-
|
|
24
|
-
[](https://pypi.org/project/askai-python/)
|
|
25
|
-
[](https://opensource.org/licenses/MIT)
|
|
26
|
-
[](https://github.com/Hosseinghorbani0/ask-ai)
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## ⚡ What is askai-python?
|
|
31
|
-
|
|
32
|
-
`askai-python` is a lightweight, zero-dependency-logic wrapper that provides a **single API** for all major AI providers. Stop learning multiple SDKs; just call `.ask()` and get your answer.
|
|
33
|
-
|
|
34
|
-
- **Unified Interface**: One syntax for OpenAI, Anthropic, Google, and more.
|
|
35
|
-
- **Zero-Config**: Seamlessly uses environment variables for API keys.
|
|
36
|
-
- **Smart Media**: Easy handling of images and audio (Provider permitting).
|
|
37
|
-
- **Production Grade**: Built-in robust error handling and type hints.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## 📦 Installation
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
pip install askai-python
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## 🚀 Examples & Tutorials
|
|
50
|
-
|
|
51
|
-
### 1. Basic Usage (The 3-Line Magic)
|
|
52
|
-
```python
|
|
53
|
-
from ask_ai import OpenAI
|
|
54
|
-
|
|
55
|
-
ai = OpenAI() # Auto-loads OPENAI_API_KEY from env
|
|
56
|
-
print(ai.ask("What is the capital of France?").text)
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 2. Multi-Provider Mastery
|
|
60
|
-
Switching between top-tier models has never been easier:
|
|
61
|
-
|
|
62
|
-
```python
|
|
63
|
-
from ask_ai import OpenAI, Anthropic, Google
|
|
64
|
-
|
|
65
|
-
prompt = "Explain quantum computing to a 5-year-old."
|
|
66
|
-
|
|
67
|
-
# OpenAI (GPT-4o)
|
|
68
|
-
print(OpenAI().ask(prompt).text)
|
|
69
|
-
|
|
70
|
-
# Anthropic (Claude 3.5 Sonnet)
|
|
71
|
-
print(Anthropic().ask(prompt).text)
|
|
72
|
-
|
|
73
|
-
# Google (Gemini 1.5 Pro)
|
|
74
|
-
print(Google().ask(prompt).text)
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 3. Advanced Configuration (System Personas)
|
|
78
|
-
You can set a system prompt and temperature globally for an instance or per-request.
|
|
79
|
-
|
|
80
|
-
```python
|
|
81
|
-
from ask_ai import OpenAI
|
|
82
|
-
|
|
83
|
-
ai = OpenAI()
|
|
84
|
-
|
|
85
|
-
# Global config
|
|
86
|
-
ai.advanced(
|
|
87
|
-
prompt="You are a sarcastic robot from the year 3000.",
|
|
88
|
-
temperature=0.9
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
print(ai.ask("What is love?").text)
|
|
92
|
-
|
|
93
|
-
# Per-request override
|
|
94
|
-
print(ai.ask("What is 1+1?", temperature=0.1).text)
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 4. Handling Structured Exceptions
|
|
98
|
-
Build reliable apps by catching specific AI errors:
|
|
99
|
-
|
|
100
|
-
```python
|
|
101
|
-
from ask_ai import OpenAI
|
|
102
|
-
from ask_ai.exceptions import AskAIError, APIKeyError
|
|
103
|
-
|
|
104
|
-
try:
|
|
105
|
-
ai = OpenAI(api_key="invalid-key")
|
|
106
|
-
ai.ask("Hello")
|
|
107
|
-
except APIKeyError:
|
|
108
|
-
print("Check your API keys!")
|
|
109
|
-
except AskAIError as e:
|
|
110
|
-
print(f"An AI error occurred: {e}")
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## 🔌 Supported Providers
|
|
116
|
-
|
|
117
|
-
| Provider | Class | Capabilities |
|
|
118
|
-
|----------|-------|-------------|
|
|
119
|
-
| **OpenAI** | `OpenAI` | Text, Images (DALL-E), Vision |
|
|
120
|
-
| **Anthropic** | `Anthropic` | Text, Vision (Claude 3.5) |
|
|
121
|
-
| **Google** | `Google` | Text, Images, Video, Audio |
|
|
122
|
-
| **Groq** | `Groq` | Ultra-fast Llama 3 & Mixtral |
|
|
123
|
-
| **Azure** | `Azure` | Enterprise-grade OpenAI |
|
|
124
|
-
| **OpenRouter**| `OpenRouter`| 100+ community models |
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## 🔗 Important Links
|
|
129
|
-
|
|
130
|
-
- **GitHub Repository**: [Hosseinghorbani0/ask-ai](https://github.com/Hosseinghorbani0/ask-ai) (Star us! ⭐)
|
|
131
|
-
- **Official Website**: [hosseinghorbani0.ir](https://hosseinghorbani0.ir/)
|
|
132
|
-
- **Bug Tracker**: [Report an Issue](https://github.com/Hosseinghorbani0/ask-ai/issues)
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
*Built with ❤️ by [Hossein Ghorbani](https://github.com/Hosseinghorbani0).*
|
|
File without changes
|
|
File without changes
|