askai-python 0.2.0__tar.gz → 0.3.3__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.
- {askai_python-0.2.0 → askai_python-0.3.3}/PKG-INFO +36 -11
- {askai_python-0.2.0 → askai_python-0.3.3}/README.md +41 -12
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/base.py +48 -7
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/exceptions.py +4 -0
- askai_python-0.3.3/ask_ai/requirements.txt +5 -0
- askai_python-0.3.3/ask_ai/utils.py +43 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/askai_python.egg-info/PKG-INFO +36 -11
- {askai_python-0.2.0 → askai_python-0.3.3}/askai_python.egg-info/SOURCES.txt +2 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/docs/PYPI_README.md +35 -10
- {askai_python-0.2.0 → askai_python-0.3.3}/pyproject.toml +8 -1
- {askai_python-0.2.0 → askai_python-0.3.3}/tests/test_core.py +1 -2
- {askai_python-0.2.0 → askai_python-0.3.3}/LICENSE +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/__init__.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/__main__.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/cli.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/config.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/media.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/providers/__init__.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/providers/anthropic_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/providers/azure_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/providers/google_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/providers/groq_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/providers/openai_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/ask_ai/providers/openrouter_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/askai_python.egg-info/dependency_links.txt +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/askai_python.egg-info/requires.txt +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/askai_python.egg-info/top_level.txt +0 -0
- {askai_python-0.2.0 → askai_python-0.3.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: askai-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more.
|
|
5
5
|
Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -53,16 +53,6 @@ print(Groq().ask("Explain black holes like I'm 5").text)
|
|
|
53
53
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
54
54
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
55
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
56
|
## 🚫 What this project is NOT
|
|
67
57
|
|
|
68
58
|
> ❌ Not an AI framework
|
|
@@ -73,8 +63,43 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
73
63
|
|
|
74
64
|
---
|
|
75
65
|
|
|
66
|
+
## 🛠️ Advanced Usage
|
|
67
|
+
|
|
68
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<img src="https://raw.githubusercontent.com/Hosseinghorbani0/ask-ai/main/assets/features.svg" alt="ask-ai features parsing banner" width="100%">
|
|
72
|
+
</p>
|
|
73
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from ask_ai import OpenAI
|
|
77
|
+
ai = OpenAI()
|
|
78
|
+
|
|
79
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
80
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
81
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
82
|
+
|
|
83
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
84
|
+
# Great for automation pipelines
|
|
85
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
86
|
+
|
|
87
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
88
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
89
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
90
|
+
|
|
91
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
92
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
93
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
94
|
+
print(data_dict['name'])
|
|
95
|
+
```
|
|
96
|
+
|
|
76
97
|
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
77
98
|
|
|
99
|
+
<p align="center">
|
|
100
|
+
<img src="https://raw.githubusercontent.com/Hosseinghorbani0/ask-ai/main/assets/resiliency.svg" alt="ask-ai resiliency banner" width="100%">
|
|
101
|
+
</p>
|
|
102
|
+
|
|
78
103
|
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
79
104
|
|
|
80
105
|
```python
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
<a href="docs/README_ja.md"><img src="https://flagcdn.com/20x15/jp.png" alt="Japanese"> 日本語</a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="assets/banner.svg" alt="ask-ai animated banner" width="100%">
|
|
18
|
+
</p>
|
|
19
|
+
|
|
15
20
|
<p align="center">
|
|
16
21
|
<b>A minimal Python SDK to switch between LLM providers in one line.</b><br/>
|
|
17
22
|
No frameworks. No servers. No overengineering.
|
|
@@ -21,6 +26,8 @@
|
|
|
21
26
|
[](https://opensource.org/licenses/MIT)
|
|
22
27
|
[](https://www.python.org/downloads/)
|
|
23
28
|
|
|
29
|
+
|
|
30
|
+
|
|
24
31
|
---
|
|
25
32
|
|
|
26
33
|
## ⚡ Quick Start (5 seconds)
|
|
@@ -48,17 +55,6 @@ Groq().ask("Explain black holes like I'm 5").text
|
|
|
48
55
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
49
56
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
50
57
|
|
|
51
|
-
## ⚖️ How it compares
|
|
52
|
-
|
|
53
|
-
| Feature | ask-ai | LangChain |
|
|
54
|
-
| -------------- | ------ | --------- |
|
|
55
|
-
| Setup time | 30 sec | 1 hour |
|
|
56
|
-
| Learning curve | ⭐ | ⭐⭐⭐⭐⭐ |
|
|
57
|
-
| Async support | ⏳ *(Coming soon)* | ⚠️ Complex |
|
|
58
|
-
| Retry/Timeout | ✅ Built-in | ❌ Manual |
|
|
59
|
-
| Gateway needed | ❌ No | ❌ No |
|
|
60
|
-
| Lines to switch| **1** | 20+ |
|
|
61
|
-
|
|
62
58
|
## 🚫 What this project is NOT
|
|
63
59
|
|
|
64
60
|
> ❌ Not an AI framework
|
|
@@ -71,7 +67,40 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
71
67
|
|
|
72
68
|
## 🛠️ Advanced Usage
|
|
73
69
|
|
|
74
|
-
###
|
|
70
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
71
|
+
|
|
72
|
+
<p align="center">
|
|
73
|
+
<img src="assets/features.svg" alt="ask-ai features parsing banner" width="100%">
|
|
74
|
+
</p>
|
|
75
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from ask_ai import OpenAI
|
|
79
|
+
ai = OpenAI()
|
|
80
|
+
|
|
81
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
82
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
83
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
84
|
+
|
|
85
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
86
|
+
# Great for automation pipelines
|
|
87
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
88
|
+
|
|
89
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
90
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
91
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
92
|
+
|
|
93
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
94
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
95
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
96
|
+
print(data_dict['name'])
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 🔄 Built-in Retries & Timeout (Resiliency)
|
|
100
|
+
|
|
101
|
+
<p align="center">
|
|
102
|
+
<img src="assets/resiliency.svg" alt="ask-ai resiliency banner" width="100%">
|
|
103
|
+
</p>
|
|
75
104
|
Handle rate limits (`429`) and network drops automatically:
|
|
76
105
|
|
|
77
106
|
```python
|
|
@@ -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
|
|
@@ -96,7 +117,10 @@ class BaseProvider:
|
|
|
96
117
|
last_error = None
|
|
97
118
|
while attempts <= retry:
|
|
98
119
|
try:
|
|
99
|
-
|
|
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
|
|
100
124
|
except Exception as e:
|
|
101
125
|
last_error = e
|
|
102
126
|
if self._is_retryable_error(e) and attempts < retry:
|
|
@@ -112,6 +136,17 @@ class BaseProvider:
|
|
|
112
136
|
from .exceptions import AskAINetworkError
|
|
113
137
|
raise AskAINetworkError(f"Request failed after {retry} retries. Last error: {last_error}")
|
|
114
138
|
|
|
139
|
+
async def ask_async(self, query: str, **kwargs) -> Response:
|
|
140
|
+
"""
|
|
141
|
+
Asynchronous wrapper for the .ask() method.
|
|
142
|
+
Perfect for Telegram bots (Aiogram) and FastAPI.
|
|
143
|
+
"""
|
|
144
|
+
import asyncio
|
|
145
|
+
loop = asyncio.get_event_loop()
|
|
146
|
+
def _sync_ask():
|
|
147
|
+
return self.ask(query, **kwargs)
|
|
148
|
+
return await loop.run_in_executor(None, _sync_ask)
|
|
149
|
+
|
|
115
150
|
def _is_retryable_error(self, e: Exception) -> bool:
|
|
116
151
|
"""Dynamically detect rate limits and network timeouts across different provider SDKs."""
|
|
117
152
|
err_str = str(e).lower()
|
|
@@ -133,10 +168,16 @@ class BaseProvider:
|
|
|
133
168
|
|
|
134
169
|
def _prepare_messages(self, query: str, config: AdvancedConfig) -> List[Dict[str, str]]:
|
|
135
170
|
messages = []
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
171
|
+
|
|
172
|
+
system_msg = config.system_message or self.persona or ""
|
|
173
|
+
|
|
174
|
+
# Smart capability: if json=True, enforce JSON system prompt
|
|
175
|
+
if config.extra.get('json'):
|
|
176
|
+
json_instruction = "You must respond with valid JSON only, without any markdown formatting or tags."
|
|
177
|
+
system_msg = f"{system_msg}\n{json_instruction}".strip()
|
|
178
|
+
|
|
179
|
+
if system_msg:
|
|
180
|
+
messages.append({"role": "system", "content": system_msg})
|
|
140
181
|
|
|
141
182
|
messages.append({"role": "user", "content": query})
|
|
142
183
|
return messages
|
|
@@ -22,5 +22,9 @@ class MediaTypeNotSupportedError(AskAIError):
|
|
|
22
22
|
"""Raised when a provider can't handle the requested media type."""
|
|
23
23
|
pass
|
|
24
24
|
|
|
25
|
+
class AskAIParsingError(AskAIError):
|
|
26
|
+
"""Raised when expect_json is True but the response cannot be parsed."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
25
29
|
# Alias for backwards compatibility internally
|
|
26
30
|
APIKeyError = AskAIConfigError
|
|
@@ -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]}...")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: askai-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more.
|
|
5
5
|
Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -53,16 +53,6 @@ print(Groq().ask("Explain black holes like I'm 5").text)
|
|
|
53
53
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
54
54
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
55
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
56
|
## 🚫 What this project is NOT
|
|
67
57
|
|
|
68
58
|
> ❌ Not an AI framework
|
|
@@ -73,8 +63,43 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
73
63
|
|
|
74
64
|
---
|
|
75
65
|
|
|
66
|
+
## 🛠️ Advanced Usage
|
|
67
|
+
|
|
68
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<img src="https://raw.githubusercontent.com/Hosseinghorbani0/ask-ai/main/assets/features.svg" alt="ask-ai features parsing banner" width="100%">
|
|
72
|
+
</p>
|
|
73
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from ask_ai import OpenAI
|
|
77
|
+
ai = OpenAI()
|
|
78
|
+
|
|
79
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
80
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
81
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
82
|
+
|
|
83
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
84
|
+
# Great for automation pipelines
|
|
85
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
86
|
+
|
|
87
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
88
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
89
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
90
|
+
|
|
91
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
92
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
93
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
94
|
+
print(data_dict['name'])
|
|
95
|
+
```
|
|
96
|
+
|
|
76
97
|
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
77
98
|
|
|
99
|
+
<p align="center">
|
|
100
|
+
<img src="https://raw.githubusercontent.com/Hosseinghorbani0/ask-ai/main/assets/resiliency.svg" alt="ask-ai resiliency banner" width="100%">
|
|
101
|
+
</p>
|
|
102
|
+
|
|
78
103
|
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
79
104
|
|
|
80
105
|
```python
|
|
@@ -34,16 +34,6 @@ print(Groq().ask("Explain black holes like I'm 5").text)
|
|
|
34
34
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
35
35
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
36
36
|
|
|
37
|
-
## ⚖️ How it compares
|
|
38
|
-
|
|
39
|
-
| Feature | ask-ai | LangChain |
|
|
40
|
-
| -------------- | ------ | --------- |
|
|
41
|
-
| Setup time | 30 sec | 1 hour |
|
|
42
|
-
| Learning curve | ⭐ | ⭐⭐⭐⭐⭐ |
|
|
43
|
-
| Async support | ⏳ *(Coming soon)* | ⚠️ Complex |
|
|
44
|
-
| Retry/Timeout | ✅ Built-in | ❌ Manual |
|
|
45
|
-
| Gateway needed | ❌ No | ❌ No |
|
|
46
|
-
|
|
47
37
|
## 🚫 What this project is NOT
|
|
48
38
|
|
|
49
39
|
> ❌ Not an AI framework
|
|
@@ -54,8 +44,43 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
54
44
|
|
|
55
45
|
---
|
|
56
46
|
|
|
47
|
+
## 🛠️ Advanced Usage
|
|
48
|
+
|
|
49
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="https://raw.githubusercontent.com/Hosseinghorbani0/ask-ai/main/assets/features.svg" alt="ask-ai features parsing banner" width="100%">
|
|
53
|
+
</p>
|
|
54
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from ask_ai import OpenAI
|
|
58
|
+
ai = OpenAI()
|
|
59
|
+
|
|
60
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
61
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
62
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
63
|
+
|
|
64
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
65
|
+
# Great for automation pipelines
|
|
66
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
67
|
+
|
|
68
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
69
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
70
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
71
|
+
|
|
72
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
73
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
74
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
75
|
+
print(data_dict['name'])
|
|
76
|
+
```
|
|
77
|
+
|
|
57
78
|
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
58
79
|
|
|
80
|
+
<p align="center">
|
|
81
|
+
<img src="https://raw.githubusercontent.com/Hosseinghorbani0/ask-ai/main/assets/resiliency.svg" alt="ask-ai resiliency banner" width="100%">
|
|
82
|
+
</p>
|
|
83
|
+
|
|
59
84
|
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
60
85
|
|
|
61
86
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "askai-python"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more."
|
|
9
9
|
readme = "docs/PYPI_README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -24,3 +24,10 @@ dependencies = [
|
|
|
24
24
|
[project.urls]
|
|
25
25
|
"Homepage" = "https://hosseinghorbani0.ir/"
|
|
26
26
|
"Bug Tracker" = "https://github.com/Hosseinghorbani0/ask-ai/issues"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
include = ["ask_ai*"]
|
|
30
|
+
exclude = ["assets*", "docs*", "tests*", ".gemini*"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.package-data]
|
|
33
|
+
"*" = ["*.txt", "*.md"]
|
|
@@ -17,8 +17,7 @@ def test_advanced_config_aliases():
|
|
|
17
17
|
def test_provider_instantiation():
|
|
18
18
|
# These should succeed with a fake key because clients are lazy-loaded or just stored
|
|
19
19
|
# We set env var to None to ensure it uses the passed key
|
|
20
|
-
|
|
21
|
-
del os.environ["OPENAI_API_KEY"]
|
|
20
|
+
os.environ.pop("OPENAI_API_KEY", None)
|
|
22
21
|
|
|
23
22
|
ai = OpenAI(api_key="sk-fake-key-for-testing")
|
|
24
23
|
assert ai.api_key == "sk-fake-key-for-testing"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|