askai-python 0.1.5__py3-none-any.whl → 0.2.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.
- ask_ai/base.py +45 -2
- ask_ai/exceptions.py +14 -3
- askai_python-0.2.0.dist-info/METADATA +120 -0
- {askai_python-0.1.5.dist-info → askai_python-0.2.0.dist-info}/RECORD +7 -7
- {askai_python-0.1.5.dist-info → askai_python-0.2.0.dist-info}/WHEEL +1 -1
- askai_python-0.1.5.dist-info/METADATA +0 -135
- {askai_python-0.1.5.dist-info → askai_python-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {askai_python-0.1.5.dist-info → askai_python-0.2.0.dist-info}/top_level.txt +0 -0
ask_ai/base.py
CHANGED
|
@@ -82,8 +82,51 @@ class BaseProvider:
|
|
|
82
82
|
# 3. Check for specific output_type override (e.g. user forced image)
|
|
83
83
|
output_type = kwargs.get('output_type')
|
|
84
84
|
|
|
85
|
-
# 4.
|
|
86
|
-
|
|
85
|
+
# 4. Extract timeout and retry safely
|
|
86
|
+
timeout_val = kwargs.get('timeout', final_config.extra.get('timeout', 30))
|
|
87
|
+
retry_val = kwargs.get('retry', final_config.extra.get('retry', 0))
|
|
88
|
+
|
|
89
|
+
timeout = float(timeout_val) if timeout_val is not None else 30.0
|
|
90
|
+
retry = int(retry_val) if retry_val is not None else 0
|
|
91
|
+
|
|
92
|
+
final_config.extra['timeout'] = timeout # Pass down to providers
|
|
93
|
+
|
|
94
|
+
# 5. Execute with Retry Logic
|
|
95
|
+
attempts = 0
|
|
96
|
+
last_error = None
|
|
97
|
+
while attempts <= retry:
|
|
98
|
+
try:
|
|
99
|
+
return self._send_request(messages, final_config, output_type)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
last_error = e
|
|
102
|
+
if self._is_retryable_error(e) and attempts < retry:
|
|
103
|
+
attempts += 1
|
|
104
|
+
delay = 2 ** attempts # 2s, 4s, 8s...
|
|
105
|
+
print(f"[ask-ai] Transient error ({e.__class__.__name__}). Retrying in {delay}s... ({attempts}/{retry})")
|
|
106
|
+
import time
|
|
107
|
+
time.sleep(delay)
|
|
108
|
+
else:
|
|
109
|
+
raise e
|
|
110
|
+
|
|
111
|
+
# Fallback if loop exits without returning
|
|
112
|
+
from .exceptions import AskAINetworkError
|
|
113
|
+
raise AskAINetworkError(f"Request failed after {retry} retries. Last error: {last_error}")
|
|
114
|
+
|
|
115
|
+
def _is_retryable_error(self, e: Exception) -> bool:
|
|
116
|
+
"""Dynamically detect rate limits and network timeouts across different provider SDKs."""
|
|
117
|
+
err_str = str(e).lower()
|
|
118
|
+
err_cls = e.__class__.__name__.lower()
|
|
119
|
+
|
|
120
|
+
# Explicit ask-ai errors
|
|
121
|
+
from .exceptions import AskAINetworkError, AskAIRateLimitError
|
|
122
|
+
if isinstance(e, (AskAINetworkError, AskAIRateLimitError)): return True
|
|
123
|
+
|
|
124
|
+
# Heuristics for underlying provider SDKs (OpenAI, Anthropic, etc.)
|
|
125
|
+
if "ratelimit" in err_cls or "rate_limit" in err_cls or "429" in err_str: return True
|
|
126
|
+
if "timeout" in err_cls or "timeout" in err_str: return True
|
|
127
|
+
if "connection" in err_cls or "connecterror" in err_cls: return True
|
|
128
|
+
|
|
129
|
+
return False
|
|
87
130
|
|
|
88
131
|
def _merge_configs(self, global_conf: AdvancedConfig, req_conf: AdvancedConfig) -> AdvancedConfig:
|
|
89
132
|
return global_conf.merge(req_conf) # We need to implement merge logic in AdvancedConfig properly or here
|
ask_ai/exceptions.py
CHANGED
|
@@ -2,14 +2,25 @@ 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
|
+
# Alias for backwards compatibility internally
|
|
26
|
+
APIKeyError = AskAIConfigError
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: askai-python
|
|
3
|
+
Version: 0.2.0
|
|
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
|
+
## ⚖️ How it compares
|
|
57
|
+
|
|
58
|
+
| Feature | ask-ai | LangChain |
|
|
59
|
+
| -------------- | ------ | --------- |
|
|
60
|
+
| Setup time | 30 sec | 1 hour |
|
|
61
|
+
| Learning curve | ⭐ | ⭐⭐⭐⭐⭐ |
|
|
62
|
+
| Async support | ⏳ *(Coming soon)* | ⚠️ Complex |
|
|
63
|
+
| Retry/Timeout | ✅ Built-in | ❌ Manual |
|
|
64
|
+
| Gateway needed | ❌ No | ❌ No |
|
|
65
|
+
|
|
66
|
+
## 🚫 What this project is NOT
|
|
67
|
+
|
|
68
|
+
> ❌ Not an AI framework
|
|
69
|
+
> ❌ Not an API gateway
|
|
70
|
+
> ❌ Not an agent memory system
|
|
71
|
+
|
|
72
|
+
It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
77
|
+
|
|
78
|
+
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from ask_ai import OpenAI
|
|
82
|
+
|
|
83
|
+
ai = OpenAI()
|
|
84
|
+
|
|
85
|
+
# Automatically retries up to 3 times on transient errors, with a 15-second timeout
|
|
86
|
+
response = ai.ask(
|
|
87
|
+
"Write a complex python script",
|
|
88
|
+
retry=3,
|
|
89
|
+
timeout=15
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 🔌 Supported Providers
|
|
94
|
+
|
|
95
|
+
| Provider | Class | Capabilities |
|
|
96
|
+
|----------|-------|-------------|
|
|
97
|
+
| **OpenAI** | `OpenAI` | Text, Images (DALL-E), Vision |
|
|
98
|
+
| **Anthropic** | `Anthropic` | Text, Vision (Claude 3.5) |
|
|
99
|
+
| **Google** | `Google` | Text, Images, Video, Audio |
|
|
100
|
+
| **Groq** | `Groq` | Ultra-fast Llama 3 & Mixtral |
|
|
101
|
+
| **Azure** | `Azure` | Enterprise-grade OpenAI |
|
|
102
|
+
| **OpenRouter**| `OpenRouter`| 100+ community models |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 🗺️ Roadmap
|
|
107
|
+
|
|
108
|
+
- [x] Baseline multiple providers
|
|
109
|
+
- [x] Automated Retry & Timeout controls
|
|
110
|
+
- [ ] Async API (`await ask_async`)
|
|
111
|
+
- [ ] Provider Fallback (`fallback=[Groq()]`)
|
|
112
|
+
- [ ] Structured Outputs (Pydantic Support)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 🔗 Important Links
|
|
117
|
+
|
|
118
|
+
- **GitHub Repository**: [Hosseinghorbani0/ask-ai](https://github.com/Hosseinghorbani0/ask-ai) (Star us! ⭐)
|
|
119
|
+
- **Official Website**: [hosseinghorbani0.ir](https://hosseinghorbani0.ir/)
|
|
120
|
+
- **Bug Tracker**: [Report an Issue](https://github.com/Hosseinghorbani0/ask-ai/issues)
|
|
@@ -1,9 +1,9 @@
|
|
|
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=N3U_O_alfhTyiIz_ts-z7YYdoJlR3yJcQxvD3Z9NNpY,7402
|
|
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=V7IVUL-oNkUYXnek0J4Bt66MaxgvJQCVk8bBTpqsMk4,769
|
|
7
7
|
ask_ai/media.py,sha256=cxPZ0XoY0iU3Y8bY-YV3MGPlNFOJ_FjjwMATgo-jcFw,1821
|
|
8
8
|
ask_ai/providers/__init__.py,sha256=tVSW-FfSMPzCYK_TZy93W5ckdVe2PBPyzogpKXH_jhg,341
|
|
9
9
|
ask_ai/providers/anthropic_provider.py,sha256=NL_6E7eNTYAoYpym4ewD8Vj8tF_ewuFjFkRR0Jj0L-U,3670
|
|
@@ -12,8 +12,8 @@ ask_ai/providers/google_provider.py,sha256=DsIgu_cBC7ta0lKclAlge1PYfcp3zXKPLjWSe
|
|
|
12
12
|
ask_ai/providers/groq_provider.py,sha256=iU7BTpvmBniE78VdTQETrCXrJzdXTpWH2oN9_sUgnFk,3268
|
|
13
13
|
ask_ai/providers/openai_provider.py,sha256=b6grPbsUIY19HkABDrUIAZAIcJCKQup5tooU2LFlkvo,4793
|
|
14
14
|
ask_ai/providers/openrouter_provider.py,sha256=LCGK-vNxy_maY2Fle6xh_i8MVm7a_bhjCaF7Uj0H3AM,1986
|
|
15
|
-
askai_python-0.
|
|
16
|
-
askai_python-0.
|
|
17
|
-
askai_python-0.
|
|
18
|
-
askai_python-0.
|
|
19
|
-
askai_python-0.
|
|
15
|
+
askai_python-0.2.0.dist-info/licenses/LICENSE,sha256=SUSbAUsh_Uqw7NDEKwAGRVPS7sojihBHIBGBx6WZxvg,1073
|
|
16
|
+
askai_python-0.2.0.dist-info/METADATA,sha256=EHGGvSOedlST4YqUG8KD3uT0rmmK3VSJt3A35dAfjqM,3609
|
|
17
|
+
askai_python-0.2.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
18
|
+
askai_python-0.2.0.dist-info/top_level.txt,sha256=fW2GapXaz6f-2UAuzVVPUPNfZUK_ZS1dpLyCllcE03s,7
|
|
19
|
+
askai_python-0.2.0.dist-info/RECORD,,
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: askai-python
|
|
3
|
-
Version: 0.1.5
|
|
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
|